Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Wilkins <Jason.A.Wilkins@gmail.com>2014-06-27 12:01:42 +0400
committerJason Wilkins <Jason.A.Wilkins@gmail.com>2014-06-27 12:01:42 +0400
commit4606950e81efe0c594f0e9908a6ab09691667255 (patch)
tree367031e502d56eb50b989f1e3ab0a2d4f388fb62
parent8ecdbe5f9861b992e1d467833a435c3fb6080348 (diff)
parent0a0e4e0e698eb496c4fb18c79b532104581ce0af (diff)
Merge branch 'master' of git.blender.org:blender into soc-2014-viewport_fx
Conflicts: CMakeLists.txt source/creator/CMakeLists.txt source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLLight.cpp
-rw-r--r--CMakeLists.txt24
-rw-r--r--GNUmakefile4
-rw-r--r--SConstruct3
-rw-r--r--build_files/buildbot/config/user-config-mac-i386.py2
-rw-r--r--build_files/buildbot/config/user-config-mac-x86_64.py2
-rw-r--r--build_files/cmake/Modules/GTestTesting.cmake47
-rw-r--r--build_files/cmake/buildinfo.cmake4
-rw-r--r--build_files/cmake/macros.cmake239
-rw-r--r--build_files/scons/config/win32-vc-config.py2
-rw-r--r--build_files/scons/config/win64-vc-config.py2
-rw-r--r--build_files/scons/tools/Blender.py39
-rw-r--r--build_files/scons/tools/btools.py21
-rw-r--r--doc/manpage/blender.1457
-rwxr-xr-x[-rw-r--r--]doc/manpage/blender.1.py40
-rw-r--r--doc/python_api/examples/mathutils.Euler.py2
-rw-r--r--doc/python_api/rst/bge.logic.rst23
-rw-r--r--doc/python_api/rst/bge_types/bge.types.KX_MouseActuator.rst103
-rw-r--r--extern/CMakeLists.txt4
-rw-r--r--extern/carve/carve-capi.cc58
-rw-r--r--extern/carve/carve-util.cc35
-rw-r--r--extern/carve/carve-util.h4
-rw-r--r--extern/carve/include/carve/mesh_simplify.hpp2
-rw-r--r--extern/carve/patches/mesh_simplify_uninitialized_var.patch12
-rw-r--r--extern/carve/patches/series1
-rw-r--r--extern/gtest/CMakeLists.txt64
-rw-r--r--extern/gtest/LICENSE28
-rw-r--r--extern/gtest/README7
-rw-r--r--extern/gtest/include/gtest/gtest-death-test.h294
-rw-r--r--extern/gtest/include/gtest/gtest-message.h250
-rw-r--r--extern/gtest/include/gtest/gtest-param-test.h1421
-rw-r--r--extern/gtest/include/gtest/gtest-printers.h855
-rw-r--r--extern/gtest/include/gtest/gtest-spi.h232
-rw-r--r--extern/gtest/include/gtest/gtest-test-part.h179
-rw-r--r--extern/gtest/include/gtest/gtest-typed-test.h259
-rw-r--r--extern/gtest/include/gtest/gtest.h2291
-rw-r--r--extern/gtest/include/gtest/gtest_pred_impl.h358
-rw-r--r--extern/gtest/include/gtest/gtest_prod.h58
-rw-r--r--extern/gtest/include/gtest/internal/gtest-death-test-internal.h319
-rw-r--r--extern/gtest/include/gtest/internal/gtest-filepath.h206
-rw-r--r--extern/gtest/include/gtest/internal/gtest-internal.h1158
-rw-r--r--extern/gtest/include/gtest/internal/gtest-linked_ptr.h233
-rw-r--r--extern/gtest/include/gtest/internal/gtest-param-util-generated.h5143
-rw-r--r--extern/gtest/include/gtest/internal/gtest-param-util.h619
-rw-r--r--extern/gtest/include/gtest/internal/gtest-port.h1947
-rw-r--r--extern/gtest/include/gtest/internal/gtest-string.h167
-rw-r--r--extern/gtest/include/gtest/internal/gtest-tuple.h1012
-rw-r--r--extern/gtest/include/gtest/internal/gtest-type-util.h3331
-rw-r--r--extern/gtest/src/gtest-all.cc48
-rw-r--r--extern/gtest/src/gtest-death-test.cc1344
-rw-r--r--extern/gtest/src/gtest-filepath.cc382
-rw-r--r--extern/gtest/src/gtest-internal-inl.h1218
-rw-r--r--extern/gtest/src/gtest-port.cc805
-rw-r--r--extern/gtest/src/gtest-printers.cc363
-rw-r--r--extern/gtest/src/gtest-test-part.cc110
-rw-r--r--extern/gtest/src/gtest-typed-test.cc110
-rw-r--r--extern/gtest/src/gtest.cc5015
-rw-r--r--extern/gtest/src/gtest_main.cc38
-rw-r--r--extern/libmv/CMakeLists.txt112
-rw-r--r--extern/libmv/SConscript2
-rwxr-xr-xextern/libmv/bundle.sh87
-rw-r--r--extern/libmv/files.txt36
-rw-r--r--extern/libmv/libmv/base/scoped_ptr_test.cc79
-rw-r--r--extern/libmv/libmv/base/vector_test.cc223
-rw-r--r--extern/libmv/libmv/image/array_nd_test.cc324
-rw-r--r--extern/libmv/libmv/image/convolve_test.cc110
-rw-r--r--extern/libmv/libmv/image/image_drawing.h285
-rw-r--r--extern/libmv/libmv/image/image_test.cc45
-rw-r--r--extern/libmv/libmv/image/sample_test.cc89
-rw-r--r--extern/libmv/libmv/image/tuple_test.cc83
-rw-r--r--extern/libmv/libmv/multiview/euclidean_resection_test.cc237
-rw-r--r--extern/libmv/libmv/multiview/fundamental_test.cc162
-rw-r--r--extern/libmv/libmv/multiview/homography_error.h248
-rw-r--r--extern/libmv/libmv/multiview/homography_test.cc261
-rw-r--r--extern/libmv/libmv/multiview/nviewtriangulation_test.cc94
-rw-r--r--extern/libmv/libmv/multiview/panography_kernel.cc51
-rw-r--r--extern/libmv/libmv/multiview/panography_kernel.h54
-rw-r--r--extern/libmv/libmv/multiview/panography_test.cc144
-rw-r--r--extern/libmv/libmv/multiview/projection_test.cc115
-rw-r--r--extern/libmv/libmv/multiview/resection_test.cc61
-rw-r--r--extern/libmv/libmv/multiview/test_data_sets.cc196
-rw-r--r--extern/libmv/libmv/multiview/test_data_sets.h105
-rw-r--r--extern/libmv/libmv/multiview/triangulation_test.cc47
-rw-r--r--extern/libmv/libmv/multiview/two_view_kernel.h137
-rw-r--r--extern/libmv/libmv/numeric/dogleg_test.cc95
-rw-r--r--extern/libmv/libmv/numeric/function_derivative_test.cc57
-rw-r--r--extern/libmv/libmv/numeric/levenberg_marquardt_test.cc56
-rw-r--r--extern/libmv/libmv/numeric/numeric_test.cc439
-rw-r--r--extern/libmv/libmv/numeric/poly_test.cc98
-rw-r--r--extern/libmv/libmv/simple_pipeline/camera_intrinsics_test.cc239
-rw-r--r--extern/libmv/libmv/simple_pipeline/detect_test.cc230
-rw-r--r--extern/libmv/libmv/simple_pipeline/intersect_test.cc81
-rw-r--r--extern/libmv/libmv/simple_pipeline/keyframe_selection_test.cc307
-rw-r--r--extern/libmv/libmv/simple_pipeline/modal_solver_test.cc79
-rw-r--r--extern/libmv/libmv/simple_pipeline/resect_test.cc234
-rw-r--r--extern/libmv/libmv/tracking/brute_region_tracker_test.cc51
-rw-r--r--extern/libmv/libmv/tracking/klt_region_tracker_test.cc51
-rw-r--r--extern/libmv/libmv/tracking/pyramid_region_tracker_test.cc80
-rw-r--r--intern/cycles/CMakeLists.txt18
-rw-r--r--intern/cycles/SConscript3
-rw-r--r--intern/cycles/app/cycles_xml.cpp12
-rw-r--r--intern/cycles/blender/addon/properties.py30
-rw-r--r--intern/cycles/blender/addon/ui.py55
-rw-r--r--intern/cycles/blender/blender_curves.cpp25
-rw-r--r--intern/cycles/blender/blender_mesh.cpp38
-rw-r--r--intern/cycles/blender/blender_shader.cpp29
-rw-r--r--intern/cycles/blender/blender_sync.cpp1
-rw-r--r--intern/cycles/device/device_cpu.cpp53
-rw-r--r--intern/cycles/kernel/CMakeLists.txt8
-rw-r--r--intern/cycles/kernel/SConscript4
-rw-r--r--intern/cycles/kernel/closure/bsdf.h34
-rw-r--r--intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h210
-rw-r--r--intern/cycles/kernel/closure/bsdf_hair.h8
-rw-r--r--intern/cycles/kernel/closure/bsdf_microfacet.h1001
-rw-r--r--intern/cycles/kernel/closure/bsdf_ward.h189
-rw-r--r--intern/cycles/kernel/geom/geom_bvh_shadow.h57
-rw-r--r--intern/cycles/kernel/geom/geom_bvh_subsurface.h54
-rw-r--r--intern/cycles/kernel/geom/geom_bvh_traversal.h56
-rw-r--r--intern/cycles/kernel/geom/geom_curve.h122
-rw-r--r--intern/cycles/kernel/geom/geom_motion_triangle.h3
-rw-r--r--intern/cycles/kernel/geom/geom_triangle.h39
-rw-r--r--intern/cycles/kernel/kernel.h11
-rw-r--r--intern/cycles/kernel/kernel_avx.cpp1
-rw-r--r--intern/cycles/kernel/kernel_avx2.cpp87
-rw-r--r--intern/cycles/kernel/kernel_bake.h16
-rw-r--r--intern/cycles/kernel/kernel_compat_cpu.h12
-rw-r--r--intern/cycles/kernel/kernel_compat_opencl.h4
-rw-r--r--intern/cycles/kernel/kernel_emission.h69
-rw-r--r--intern/cycles/kernel/kernel_light.h26
-rw-r--r--intern/cycles/kernel/kernel_path.h791
-rw-r--r--intern/cycles/kernel/kernel_path_state.h2
-rw-r--r--intern/cycles/kernel/kernel_path_surface.h299
-rw-r--r--intern/cycles/kernel/kernel_path_volume.h281
-rw-r--r--intern/cycles/kernel/kernel_random.h18
-rw-r--r--intern/cycles/kernel/kernel_shader.h15
-rw-r--r--intern/cycles/kernel/kernel_textures.h3
-rw-r--r--intern/cycles/kernel/kernel_types.h36
-rw-r--r--intern/cycles/kernel/kernel_volume.h364
-rw-r--r--intern/cycles/kernel/osl/osl_closures.cpp42
-rw-r--r--intern/cycles/kernel/osl/osl_closures.h11
-rw-r--r--intern/cycles/kernel/osl/osl_services.cpp26
-rw-r--r--intern/cycles/kernel/osl/osl_shader.cpp8
-rw-r--r--intern/cycles/kernel/shaders/CMakeLists.txt4
-rw-r--r--intern/cycles/kernel/shaders/node_anisotropic_bsdf.osl (renamed from intern/cycles/kernel/shaders/node_ward_bsdf.osl)12
-rw-r--r--intern/cycles/kernel/shaders/node_checker_texture.osl6
-rw-r--r--intern/cycles/kernel/shaders/node_combine_xyz.osl27
-rw-r--r--intern/cycles/kernel/shaders/node_glossy_bsdf.osl4
-rw-r--r--intern/cycles/kernel/shaders/node_separate_xyz.osl28
-rw-r--r--intern/cycles/kernel/shaders/stdosl.h4
-rw-r--r--intern/cycles/kernel/svm/svm.h10
-rw-r--r--intern/cycles/kernel/svm/svm_blackbody.h2
-rw-r--r--intern/cycles/kernel/svm/svm_checker.h6
-rw-r--r--intern/cycles/kernel/svm/svm_closure.h67
-rw-r--r--intern/cycles/kernel/svm/svm_image.h12
-rw-r--r--intern/cycles/kernel/svm/svm_noise.h144
-rw-r--r--intern/cycles/kernel/svm/svm_sepcomb_rgb.h42
-rw-r--r--intern/cycles/kernel/svm/svm_sepcomb_vector.h44
-rw-r--r--intern/cycles/kernel/svm/svm_texture.h22
-rw-r--r--intern/cycles/kernel/svm/svm_types.h11
-rw-r--r--intern/cycles/render/attribute.cpp10
-rw-r--r--intern/cycles/render/attribute.h2
-rw-r--r--intern/cycles/render/curves.cpp16
-rw-r--r--intern/cycles/render/image.cpp64
-rw-r--r--intern/cycles/render/image.h6
-rw-r--r--intern/cycles/render/integrator.cpp12
-rw-r--r--intern/cycles/render/mesh.cpp51
-rw-r--r--intern/cycles/render/mesh.h2
-rw-r--r--intern/cycles/render/nodes.cpp174
-rw-r--r--intern/cycles/render/nodes.h18
-rw-r--r--intern/cycles/render/scene.h3
-rw-r--r--intern/cycles/render/shader.cpp113
-rw-r--r--intern/cycles/render/shader.h2
-rw-r--r--intern/cycles/render/tile.cpp2
-rw-r--r--intern/cycles/util/CMakeLists.txt4
-rw-r--r--intern/cycles/util/util_color.h67
-rw-r--r--intern/cycles/util/util_half.h24
-rw-r--r--intern/cycles/util/util_math.h4
-rw-r--r--intern/cycles/util/util_optimization.h15
-rw-r--r--intern/cycles/util/util_simd.cpp47
-rw-r--r--intern/cycles/util/util_simd.h479
-rw-r--r--intern/cycles/util/util_sseb.h161
-rw-r--r--intern/cycles/util/util_ssef.h588
-rw-r--r--intern/cycles/util/util_ssei.h294
-rw-r--r--intern/cycles/util/util_system.cpp17
-rw-r--r--intern/cycles/util/util_system.h1
-rw-r--r--intern/cycles/util/util_types.h2
-rw-r--r--intern/ghost/GHOST_C-api.h2
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp2
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.mm2
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp11
-rw-r--r--intern/ghost/intern/GHOST_WindowCocoa.mm2
-rw-r--r--intern/guardedalloc/MEM_guardedalloc.h6
-rw-r--r--intern/guardedalloc/intern/mallocn.c38
-rw-r--r--intern/guardedalloc/intern/mallocn_guarded_impl.c95
-rw-r--r--intern/guardedalloc/intern/mallocn_intern.h31
-rw-r--r--intern/guardedalloc/intern/mallocn_lockfree_impl.c111
-rw-r--r--release/darwin/codesigning_rules_blender.plist2
-rw-r--r--release/darwin/codesigning_rules_player.plist2
-rw-r--r--release/datafiles/brushicons/texfill.pngbin0 -> 8474 bytes
-rw-r--r--release/datafiles/brushicons/texmask.pngbin0 -> 7791 bytes
m---------release/datafiles/locale0
-rw-r--r--release/datafiles/splash.pngbin206467 -> 254208 bytes
-rw-r--r--release/datafiles/splash_2x.pngbin660326 -> 890649 bytes
m---------release/scripts/addons0
m---------release/scripts/addons_contrib0
-rw-r--r--release/scripts/freestyle/modules/freestyle/chainingiterators.py774
-rw-r--r--release/scripts/freestyle/modules/freestyle/functions.py33
-rw-r--r--release/scripts/freestyle/modules/freestyle/predicates.py290
-rw-r--r--release/scripts/freestyle/modules/freestyle/shaders.py1460
-rw-r--r--release/scripts/freestyle/modules/freestyle/utils.py301
-rw-r--r--release/scripts/modules/bl_i18n_utils/utils_spell_check.py15
-rw-r--r--release/scripts/modules/bpy/utils.py2
-rw-r--r--release/scripts/modules/nodeitems_utils.py2
-rw-r--r--release/scripts/presets/keyconfig/maya.py4
-rw-r--r--release/scripts/startup/bl_operators/wm.py9
-rw-r--r--release/scripts/startup/bl_ui/properties_texture.py2
-rw-r--r--release/scripts/startup/bl_ui/space_info.py2
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py4
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py3
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py39
-rw-r--r--release/scripts/startup/nodeitems_builtins.py9
-rw-r--r--release/text/readme.html10
m---------scons0
-rw-r--r--source/CMakeLists.txt1
-rw-r--r--source/blender/blenfont/BLF_api.h56
-rw-r--r--source/blender/blenfont/intern/blf.c23
-rw-r--r--source/blender/blenkernel/BKE_blender.h4
-rw-r--r--source/blender/blenkernel/BKE_depsgraph.h2
-rw-r--r--source/blender/blenkernel/BKE_displist.h1
-rw-r--r--source/blender/blenkernel/BKE_library.h5
-rw-r--r--source/blender/blenkernel/BKE_main.h3
-rw-r--r--source/blender/blenkernel/BKE_mesh_mapping.h3
-rw-r--r--source/blender/blenkernel/BKE_modifier.h5
-rw-r--r--source/blender/blenkernel/BKE_multires.h3
-rw-r--r--source/blender/blenkernel/BKE_node.h6
-rw-r--r--source/blender/blenkernel/BKE_pbvh.h2
-rw-r--r--source/blender/blenkernel/BKE_unit.h24
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.c12
-rw-r--r--source/blender/blenkernel/intern/anim.c2
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c6
-rw-r--r--source/blender/blenkernel/intern/blender.c9
-rw-r--r--source/blender/blenkernel/intern/bpath.c2
-rw-r--r--source/blender/blenkernel/intern/cdderivedmesh.c23
-rw-r--r--source/blender/blenkernel/intern/cloth.c6
-rw-r--r--source/blender/blenkernel/intern/constraint.c7
-rw-r--r--source/blender/blenkernel/intern/context.c2
-rw-r--r--source/blender/blenkernel/intern/crazyspace.c4
-rw-r--r--source/blender/blenkernel/intern/curve.c94
-rw-r--r--source/blender/blenkernel/intern/displist.c36
-rw-r--r--source/blender/blenkernel/intern/editderivedmesh.c4
-rw-r--r--source/blender/blenkernel/intern/fcurve.c14
-rw-r--r--source/blender/blenkernel/intern/idprop.c2
-rw-r--r--source/blender/blenkernel/intern/image.c2
-rw-r--r--source/blender/blenkernel/intern/lattice.c19
-rw-r--r--source/blender/blenkernel/intern/library.c29
-rw-r--r--source/blender/blenkernel/intern/material.c2
-rw-r--r--source/blender/blenkernel/intern/mball.c4
-rw-r--r--source/blender/blenkernel/intern/mesh.c14
-rw-r--r--source/blender/blenkernel/intern/mesh_evaluate.c4
-rw-r--r--source/blender/blenkernel/intern/modifier.c4
-rw-r--r--source/blender/blenkernel/intern/multires.c35
-rw-r--r--source/blender/blenkernel/intern/navmesh_conversion.c4
-rw-r--r--source/blender/blenkernel/intern/node.c4
-rw-r--r--source/blender/blenkernel/intern/object.c7
-rw-r--r--source/blender/blenkernel/intern/paint.c8
-rw-r--r--source/blender/blenkernel/intern/particle_system.c4
-rw-r--r--source/blender/blenkernel/intern/pbvh.c6
-rw-r--r--source/blender/blenkernel/intern/pbvh_bmesh.c2
-rw-r--r--source/blender/blenkernel/intern/pointcache.c4
-rw-r--r--source/blender/blenkernel/intern/sca.c10
-rw-r--r--source/blender/blenkernel/intern/scene.c2
-rw-r--r--source/blender/blenkernel/intern/seqeffects.c8
-rw-r--r--source/blender/blenkernel/intern/sequencer.c8
-rw-r--r--source/blender/blenkernel/intern/smoke.c2
-rw-r--r--source/blender/blenkernel/intern/subsurf_ccg.c6
-rw-r--r--source/blender/blenkernel/intern/tracking_stabilize.c51
-rw-r--r--source/blender/blenkernel/intern/unit.c4
-rw-r--r--source/blender/blenlib/BLI_array.h4
-rw-r--r--source/blender/blenlib/BLI_bitmap.h52
-rw-r--r--source/blender/blenlib/BLI_dynstr.h76
-rw-r--r--source/blender/blenlib/BLI_edgehash.h2
-rw-r--r--source/blender/blenlib/BLI_fileops.h12
-rw-r--r--source/blender/blenlib/BLI_fileops_types.h10
-rw-r--r--source/blender/blenlib/BLI_ghash.h3
-rw-r--r--source/blender/blenlib/BLI_gsqueue.h10
-rw-r--r--source/blender/blenlib/BLI_linklist_stack.h4
-rw-r--r--source/blender/blenlib/BLI_listbase.h2
-rw-r--r--source/blender/blenlib/BLI_math_matrix.h13
-rw-r--r--source/blender/blenlib/BLI_md5.h2
-rw-r--r--source/blender/blenlib/BLI_path_util.h6
-rw-r--r--source/blender/blenlib/BLI_polyfill2d.h10
-rw-r--r--source/blender/blenlib/BLI_rand.h1
-rw-r--r--source/blender/blenlib/BLI_sort.h10
-rw-r--r--source/blender/blenlib/BLI_stackdefines.h55
-rw-r--r--source/blender/blenlib/BLI_string.h3
-rw-r--r--source/blender/blenlib/BLI_threads.h2
-rw-r--r--source/blender/blenlib/BLI_utildefines.h63
-rw-r--r--source/blender/blenlib/BLI_winstuff.h5
-rw-r--r--source/blender/blenlib/intern/BLI_dynstr.c51
-rw-r--r--source/blender/blenlib/intern/BLI_ghash.c19
-rw-r--r--source/blender/blenlib/intern/BLI_kdtree.c6
-rw-r--r--source/blender/blenlib/intern/BLI_mempool.c4
-rw-r--r--source/blender/blenlib/intern/easing.c6
-rw-r--r--source/blender/blenlib/intern/edgehash.c7
-rw-r--r--source/blender/blenlib/intern/fileops.c72
-rw-r--r--source/blender/blenlib/intern/gsqueue.c29
-rw-r--r--source/blender/blenlib/intern/listbase.c2
-rw-r--r--source/blender/blenlib/intern/math_rotation.c63
-rw-r--r--source/blender/blenlib/intern/md5.c687
-rw-r--r--source/blender/blenlib/intern/path_util.c79
-rw-r--r--source/blender/blenlib/intern/polyfill2d.c741
-rw-r--r--source/blender/blenlib/intern/rand.c7
-rw-r--r--source/blender/blenlib/intern/sort.c11
-rw-r--r--source/blender/blenlib/intern/storage.c47
-rw-r--r--source/blender/blenlib/intern/string.c58
-rw-r--r--source/blender/blenlib/intern/winstuff_dir.c9
-rw-r--r--source/blender/blenloader/intern/readfile.c5
-rw-r--r--source/blender/blenloader/intern/versioning_260.c2
-rw-r--r--source/blender/blenloader/intern/versioning_270.c74
-rw-r--r--source/blender/blenloader/intern/writefile.c3
-rw-r--r--source/blender/bmesh/intern/bmesh_core.c3
-rw-r--r--source/blender/bmesh/intern/bmesh_edgeloop.c2
-rw-r--r--source/blender/bmesh/intern/bmesh_error.h25
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_conv.c10
-rw-r--r--source/blender/bmesh/intern/bmesh_opdefines.c2
-rw-r--r--source/blender/bmesh/intern/bmesh_operators.c2
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon.c6
-rw-r--r--source/blender/bmesh/intern/bmesh_queries.c1
-rw-r--r--source/blender/bmesh/intern/bmesh_queries_inline.h13
-rw-r--r--source/blender/bmesh/intern/bmesh_walkers_impl.c6
-rw-r--r--source/blender/bmesh/operators/bmo_bisect_plane.c2
-rw-r--r--source/blender/bmesh/operators/bmo_connect.c1
-rw-r--r--source/blender/bmesh/operators/bmo_dissolve.c153
-rw-r--r--source/blender/bmesh/operators/bmo_extrude.c21
-rw-r--r--source/blender/bmesh/operators/bmo_removedoubles.c4
-rw-r--r--source/blender/bmesh/operators/bmo_subdivide.c9
-rw-r--r--source/blender/bmesh/operators/bmo_subdivide_edgering.c1
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c85
-rw-r--r--source/blender/bmesh/tools/bmesh_bisect_plane.c5
-rw-r--r--source/blender/bmesh/tools/bmesh_edgenet.c17
-rw-r--r--source/blender/compositor/intern/COM_Debug.cpp2
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.cpp4
-rw-r--r--source/blender/compositor/operations/COM_BlurBaseOperation.cpp12
-rw-r--r--source/blender/compositor/operations/COM_BlurBaseOperation.h7
-rw-r--r--source/blender/compositor/operations/COM_GaussianXBlurOperation.cpp37
-rw-r--r--source/blender/compositor/operations/COM_GaussianXBlurOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_GaussianYBlurOperation.cpp38
-rw-r--r--source/blender/compositor/operations/COM_GaussianYBlurOperation.h3
-rw-r--r--source/blender/datatoc/datatoc_icon.c3
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c4
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c191
-rw-r--r--source/blender/editors/animation/anim_deps.c9
-rw-r--r--source/blender/editors/animation/anim_filter.c2
-rw-r--r--source/blender/editors/animation/drivers.c2
-rw-r--r--source/blender/editors/animation/keyframing.c31
-rw-r--r--source/blender/editors/armature/pose_lib.c2
-rw-r--r--source/blender/editors/curve/editcurve.c12
-rw-r--r--source/blender/editors/curve/editcurve_add.c5
-rw-r--r--source/blender/editors/gpencil/drawgpencil.c19
-rw-r--r--source/blender/editors/gpencil/editaction_gpencil.c18
-rw-r--r--source/blender/editors/gpencil/gpencil_buttons.c6
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c28
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_undo.c2
-rw-r--r--source/blender/editors/include/ED_anim_api.h10
-rw-r--r--source/blender/editors/include/ED_gpencil.h23
-rw-r--r--source/blender/editors/include/ED_paint.h61
-rw-r--r--source/blender/editors/include/ED_screen.h1
-rw-r--r--source/blender/editors/include/ED_sculpt.h40
-rw-r--r--source/blender/editors/include/ED_view3d.h4
-rw-r--r--source/blender/editors/include/UI_interface.h2
-rw-r--r--source/blender/editors/interface/interface.c113
-rw-r--r--source/blender/editors/interface/interface_draw.c4
-rw-r--r--source/blender/editors/interface/interface_handlers.c65
-rw-r--r--source/blender/editors/interface/interface_intern.h21
-rw-r--r--source/blender/editors/interface/interface_layout.c10
-rw-r--r--source/blender/editors/interface/interface_regions.c349
-rw-r--r--source/blender/editors/interface/interface_style.c22
-rw-r--r--source/blender/editors/interface/interface_templates.c30
-rw-r--r--source/blender/editors/interface/interface_utils.c2
-rw-r--r--source/blender/editors/interface/interface_widgets.c71
-rw-r--r--source/blender/editors/interface/resources.c7
-rw-r--r--source/blender/editors/interface/view2d.c14
-rw-r--r--source/blender/editors/mask/mask_ops.c18
-rw-r--r--source/blender/editors/mask/mask_select.c2
-rw-r--r--source/blender/editors/mesh/CMakeLists.txt1
-rw-r--r--source/blender/editors/mesh/editface.c12
-rw-r--r--source/blender/editors/mesh/editmesh_bevel.c16
-rw-r--r--source/blender/editors/mesh/editmesh_extrude.c2
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c33
-rw-r--r--source/blender/editors/mesh/editmesh_rip.c2
-rw-r--r--source/blender/editors/mesh/editmesh_rip_edge.c244
-rw-r--r--source/blender/editors/mesh/editmesh_select.c33
-rw-r--r--source/blender/editors/mesh/mesh_intern.h1
-rw-r--r--source/blender/editors/mesh/mesh_ops.c10
-rw-r--r--source/blender/editors/object/object_bake_api.c160
-rw-r--r--source/blender/editors/object/object_edit.c30
-rw-r--r--source/blender/editors/object/object_lattice.c4
-rw-r--r--source/blender/editors/object/object_modifier.c4
-rw-r--r--source/blender/editors/object/object_relations.c2
-rw-r--r--source/blender/editors/object/object_select.c37
-rw-r--r--source/blender/editors/physics/particle_edit.c9
-rw-r--r--source/blender/editors/physics/particle_object.c55
-rw-r--r--source/blender/editors/render/render_internal.c66
-rw-r--r--source/blender/editors/render/render_opengl.c33
-rw-r--r--source/blender/editors/render/render_preview.c15
-rw-r--r--source/blender/editors/screen/area.c12
-rw-r--r--source/blender/editors/screen/screen_ops.c3
-rw-r--r--source/blender/editors/sculpt_paint/paint_hide.c4
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c11
-rw-r--r--source/blender/editors/sculpt_paint/paint_undo.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c137
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c9
-rw-r--r--source/blender/editors/space_action/action_ops.c3
-rw-r--r--source/blender/editors/space_action/space_action.c9
-rw-r--r--source/blender/editors/space_api/spacetypes.c1
-rw-r--r--source/blender/editors/space_buttons/buttons_texture.c2
-rw-r--r--source/blender/editors/space_clip/clip_buttons.c4
-rw-r--r--source/blender/editors/space_clip/clip_draw.c4
-rw-r--r--source/blender/editors/space_clip/tracking_select.c2
-rw-r--r--source/blender/editors/space_graph/graph_ops.c4
-rw-r--r--source/blender/editors/space_image/image_buttons.c4
-rw-r--r--source/blender/editors/space_image/image_draw.c4
-rw-r--r--source/blender/editors/space_image/image_ops.c13
-rw-r--r--source/blender/editors/space_logic/logic_window.c72
-rw-r--r--source/blender/editors/space_nla/nla_ops.c3
-rw-r--r--source/blender/editors/space_node/drawnode.c8
-rw-r--r--source/blender/editors/space_node/node_buttons.c4
-rw-r--r--source/blender/editors/space_node/node_draw.c4
-rw-r--r--source/blender/editors/space_node/node_select.c3
-rw-r--r--source/blender/editors/space_node/node_templates.c1
-rw-r--r--source/blender/editors/space_sequencer/sequencer_buttons.c4
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c4
-rw-r--r--source/blender/editors/space_text/space_text.c8
-rw-r--r--source/blender/editors/space_view3d/drawmesh.c15
-rw-r--r--source/blender/editors/space_view3d/drawobject.c554
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c53
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c10
-rw-r--r--source/blender/editors/space_view3d/view3d_intern.h6
-rw-r--r--source/blender/editors/space_view3d/view3d_ops.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_project.c5
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c34
-rw-r--r--source/blender/editors/transform/transform.c10
-rw-r--r--source/blender/editors/transform/transform_conversions.c58
-rw-r--r--source/blender/editors/transform/transform_input.c8
-rw-r--r--source/blender/editors/transform/transform_snap.c15
-rw-r--r--source/blender/editors/util/CMakeLists.txt1
-rw-r--r--source/blender/editors/util/ed_util.c44
-rw-r--r--source/blender/editors/util/numinput.c3
-rw-r--r--source/blender/editors/util/undo.c10
-rw-r--r--source/blender/editors/uvedit/uvedit_draw.c4
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c2
-rw-r--r--source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp19
-rw-r--r--source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp41
-rw-r--r--source/blender/freestyle/intern/python/BPy_Convert.cpp52
-rw-r--r--source/blender/freestyle/intern/python/BPy_Convert.h3
-rw-r--r--source/blender/freestyle/intern/python/BPy_Freestyle.cpp14
-rw-r--r--source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp25
-rw-r--r--source/blender/freestyle/intern/python/BPy_Interface0D.cpp2
-rw-r--r--source/blender/freestyle/intern/python/BPy_Interface0D.h2
-rw-r--r--source/blender/freestyle/intern/python/BPy_Interface1D.cpp10
-rw-r--r--source/blender/freestyle/intern/python/BPy_Interface1D.h2
-rw-r--r--source/blender/freestyle/intern/python/BPy_SShape.cpp2
-rw-r--r--source/blender/freestyle/intern/python/BPy_SShape.h2
-rw-r--r--source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp12
-rw-r--r--source/blender/freestyle/intern/python/BPy_StrokeAttribute.h2
-rw-r--r--source/blender/freestyle/intern/python/BPy_ViewShape.cpp2
-rw-r--r--source/blender/freestyle/intern/python/BPy_ViewShape.h2
-rw-r--r--source/blender/freestyle/intern/python/Director.cpp4
-rw-r--r--source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp2
-rw-r--r--source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp17
-rw-r--r--source/blender/freestyle/intern/python/Interface0D/BPy_ViewVertex.cpp4
-rw-r--r--source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp7
-rw-r--r--source/blender/freestyle/intern/python/Interface0D/ViewVertex/BPy_NonTVertex.cpp2
-rw-r--r--source/blender/freestyle/intern/python/Interface0D/ViewVertex/BPy_TVertex.cpp2
-rw-r--r--source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp2
-rw-r--r--source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp2
-rw-r--r--source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp22
-rw-r--r--source/blender/freestyle/intern/python/Interface1D/BPy_ViewEdge.cpp2
-rw-r--r--source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp2
-rw-r--r--source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp12
-rw-r--r--source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp7
-rw-r--r--source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp15
-rw-r--r--source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp101
-rw-r--r--source/blender/freestyle/intern/python/StrokeShader/BPy_CalligraphicShader.cpp5
-rw-r--r--source/blender/freestyle/intern/winged_edge/Curvature.cpp2
-rw-r--r--source/blender/freestyle/intern/winged_edge/WEdge.cpp5
-rw-r--r--source/blender/freestyle/intern/winged_edge/WEdge.h2
-rw-r--r--source/blender/gpu/GPU_extensions.h3
-rw-r--r--source/blender/gpu/intern/gpu_extensions.c63
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material.glsl14
-rw-r--r--source/blender/imbuf/intern/colormanagement.c4
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_api.cpp63
-rw-r--r--source/blender/imbuf/intern/thumbs.c17
-rw-r--r--source/blender/makesdna/DNA_actuator_types.h31
-rw-r--r--source/blender/makesdna/DNA_curve_types.h23
-rw-r--r--source/blender/makesdna/DNA_node_types.h7
-rw-r--r--source/blender/makesdna/DNA_scene_types.h5
-rw-r--r--source/blender/makesdna/DNA_screen_types.h2
-rw-r--r--source/blender/makesdna/DNA_sensor_types.h2
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h2
-rw-r--r--source/blender/makesrna/intern/rna_actuator.c131
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c5
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c43
-rw-r--r--source/blender/makesrna/intern/rna_scene.c14
-rw-r--r--source/blender/makesrna/intern/rna_sensor.c4
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c10
-rw-r--r--source/blender/makesrna/intern/rna_sequencer_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c9
-rw-r--r--source/blender/modifiers/intern/MOD_explode.c2
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciandeform.c4
-rw-r--r--source/blender/modifiers/intern/MOD_multires.c6
-rw-r--r--source/blender/modifiers/intern/MOD_skin.c22
-rw-r--r--source/blender/modifiers/intern/MOD_solidify.c10
-rw-r--r--source/blender/modifiers/intern/MOD_subsurf.c4
-rw-r--r--source/blender/nodes/CMakeLists.txt1
-rw-r--r--source/blender/nodes/NOD_shader.h2
-rw-r--r--source/blender/nodes/NOD_static_types.h8
-rw-r--r--source/blender/nodes/shader/node_shader_tree.c4
-rw-r--r--source/blender/nodes/shader/node_shader_util.c42
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.c7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_glass.c7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.c7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_hair.c3
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.c7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.c93
-rw-r--r--source/blender/python/generic/py_capi_utils.c83
-rw-r--r--source/blender/python/generic/py_capi_utils.h2
-rw-r--r--source/blender/python/intern/CMakeLists.txt2
-rw-r--r--source/blender/python/intern/bpy.c2
-rw-r--r--source/blender/python/intern/bpy_app.c4
-rw-r--r--source/blender/python/intern/bpy_app_build_options.c4
-rw-r--r--source/blender/python/intern/bpy_app_ffmpeg.c2
-rw-r--r--source/blender/python/intern/bpy_app_handlers.c4
-rw-r--r--source/blender/python/intern/bpy_app_ocio.c2
-rw-r--r--source/blender/python/intern/bpy_app_oiio.c2
-rw-r--r--source/blender/python/intern/bpy_app_translations.c4
-rw-r--r--source/blender/python/intern/bpy_interface.c64
-rw-r--r--source/blender/python/intern/bpy_intern_string.c4
-rw-r--r--source/blender/python/intern/bpy_utils_units.c332
-rw-r--r--source/blender/python/intern/bpy_utils_units.h32
-rw-r--r--source/blender/render/extern/include/RE_bake.h8
-rw-r--r--source/blender/render/extern/include/RE_pipeline.h5
-rw-r--r--source/blender/render/intern/include/renderpipeline.h2
-rw-r--r--source/blender/render/intern/source/bake_api.c173
-rw-r--r--source/blender/render/intern/source/convertblender.c4
-rw-r--r--source/blender/render/intern/source/external_engine.c6
-rw-r--r--source/blender/render/intern/source/occlusion.c2
-rw-r--r--source/blender/render/intern/source/pipeline.c124
-rw-r--r--source/blender/render/intern/source/render_result.c8
-rw-r--r--source/blender/windowmanager/WM_api.h2
-rw-r--r--source/blender/windowmanager/intern/wm_cursors.c2
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c2
-rw-r--r--source/blender/windowmanager/intern/wm_files.c10
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c22
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c33
-rw-r--r--source/blender/windowmanager/intern/wm_window.c3
-rw-r--r--source/blenderplayer/CMakeLists.txt1
-rw-r--r--source/blenderplayer/bad_level_call_stubs/stubs.c1
-rw-r--r--source/creator/CMakeLists.txt254
-rw-r--r--source/creator/creator.c18
-rw-r--r--source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp4
-rw-r--r--source/gameengine/Converter/KX_ConvertActuators.cpp45
-rw-r--r--source/gameengine/Converter/KX_ConvertSensors.cpp6
-rw-r--r--source/gameengine/Expressions/BoolValue.cpp7
-rw-r--r--source/gameengine/Expressions/BoolValue.h1
-rw-r--r--source/gameengine/Expressions/EmptyValue.cpp7
-rw-r--r--source/gameengine/Expressions/EmptyValue.h1
-rw-r--r--source/gameengine/Expressions/ErrorValue.cpp7
-rw-r--r--source/gameengine/Expressions/ErrorValue.h1
-rw-r--r--source/gameengine/Expressions/FloatValue.cpp7
-rw-r--r--source/gameengine/Expressions/FloatValue.h1
-rw-r--r--source/gameengine/Expressions/IntValue.cpp9
-rw-r--r--source/gameengine/Expressions/IntValue.h1
-rw-r--r--source/gameengine/Expressions/ListValue.cpp7
-rw-r--r--source/gameengine/Expressions/ListValue.h1
-rw-r--r--source/gameengine/Expressions/StringValue.cpp7
-rw-r--r--source/gameengine/Expressions/StringValue.h1
-rw-r--r--source/gameengine/Expressions/Value.cpp9
-rw-r--r--source/gameengine/Expressions/Value.h8
-rw-r--r--source/gameengine/Expressions/VectorValue.cpp8
-rw-r--r--source/gameengine/Expressions/VectorValue.h1
-rw-r--r--source/gameengine/Expressions/VoidValue.h1
-rw-r--r--source/gameengine/GameLogic/SCA_IActuator.h1
-rw-r--r--source/gameengine/GameLogic/SCA_PropertySensor.cpp39
-rw-r--r--source/gameengine/GameLogic/SCA_PropertySensor.h2
-rw-r--r--source/gameengine/GamePlayer/ghost/GPG_ghost.cpp4
-rw-r--r--source/gameengine/Ketsji/CMakeLists.txt2
-rw-r--r--source/gameengine/Ketsji/KX_KetsjiEngine.cpp4
-rw-r--r--source/gameengine/Ketsji/KX_MouseActuator.cpp525
-rw-r--r--source/gameengine/Ketsji/KX_MouseActuator.h127
-rw-r--r--source/gameengine/Ketsji/KX_ObjectActuator.cpp1
-rw-r--r--source/gameengine/Ketsji/KX_PythonInit.cpp9
-rw-r--r--source/gameengine/Ketsji/KX_PythonInitTypes.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_RaySensor.h13
-rw-r--r--source/gameengine/Ketsji/KX_Scene.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_TouchEventManager.cpp7
-rw-r--r--source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLLight.cpp3
-rw-r--r--tests/CMakeLists.txt7
-rw-r--r--tests/check_deprecated.py (renamed from source/tests/check_deprecated.py)4
-rw-r--r--tests/gtests/CMakeLists.txt15
-rw-r--r--tests/gtests/blenlib/BLI_math_color_test.cc90
-rw-r--r--tests/gtests/blenlib/BLI_math_geom_test.cc23
-rw-r--r--tests/gtests/blenlib/BLI_path_util_test.cc221
-rw-r--r--tests/gtests/blenlib/CMakeLists.txt39
-rw-r--r--tests/gtests/bmesh/CMakeLists.txt50
-rw-r--r--tests/gtests/bmesh/bmesh_core_test.cc36
-rw-r--r--tests/gtests/guardedalloc/CMakeLists.txt36
-rw-r--r--tests/gtests/guardedalloc/guardedalloc_alignment_test.cc53
-rw-r--r--tests/gtests/testing/CMakeLists.txt48
-rw-r--r--tests/gtests/testing/testing.h78
-rw-r--r--tests/gtests/testing/testing_main.cc36
-rw-r--r--tests/python/CMakeLists.txt (renamed from source/tests/CMakeLists.txt)0
-rw-r--r--tests/python/batch_import.py (renamed from source/tests/batch_import.py)6
-rw-r--r--tests/python/bl_keymap_completeness.py (renamed from source/tests/bl_keymap_completeness.py)0
-rw-r--r--tests/python/bl_load_addons.py (renamed from source/tests/bl_load_addons.py)0
-rw-r--r--tests/python/bl_load_py_modules.py (renamed from source/tests/bl_load_py_modules.py)0
-rw-r--r--tests/python/bl_mesh_modifiers.py (renamed from source/tests/bl_mesh_modifiers.py)2
-rw-r--r--tests/python/bl_mesh_validate.py (renamed from source/tests/bl_mesh_validate.py)0
-rw-r--r--tests/python/bl_pyapi_mathutils.py (renamed from source/tests/bl_pyapi_mathutils.py)4
-rw-r--r--tests/python/bl_pyapi_units.py82
-rw-r--r--tests/python/bl_rna_wiki_reference.py (renamed from source/tests/bl_rna_wiki_reference.py)2
-rw-r--r--tests/python/bl_rst_completeness.py (renamed from source/tests/bl_rst_completeness.py)4
-rw-r--r--tests/python/bl_run_operators.py (renamed from source/tests/bl_run_operators.py)0
-rw-r--r--tests/python/bl_test.py (renamed from source/tests/bl_test.py)0
-rw-r--r--tests/python/pep8.py (renamed from source/tests/pep8.py)2
-rw-r--r--tests/python/rna_array.py (renamed from source/tests/rna_array.py)18
-rw-r--r--tests/python/rna_info_dump.py (renamed from source/tests/rna_info_dump.py)2
-rw-r--r--tests/python/rst_to_doctree_mini.py (renamed from source/tests/rst_to_doctree_mini.py)0
636 files changed, 50077 insertions, 7090 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d21df763673..7325fbc337c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -47,6 +47,11 @@ endif()
cmake_minimum_required(VERSION 2.8)
+if(NOT (${CMAKE_VERSION} VERSION_LESS 3.0))
+ # keep until CMake-3.0 is min requirement
+ cmake_policy(SET CMP0043 OLD)
+endif()
+
if(NOT EXECUTABLE_OUTPUT_PATH)
set(FIRST_RUN "TRUE")
endif()
@@ -104,6 +109,7 @@ enable_testing()
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin CACHE INTERNAL "" FORCE)
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib CACHE INTERNAL "" FORCE)
+set(TESTS_OUTPUT_DIR ${EXECUTABLE_OUTPUT_PATH}/tests CACHE INTERNAL "" FORCE)
#-----------------------------------------------------------------------------
# Set default config options
@@ -298,6 +304,9 @@ if(CMAKE_COMPILER_IS_GNUCC)
mark_as_advanced(WITH_GCC_MUDFLAP)
endif()
+# Unit testsing
+option(WITH_GTESTS "Enable GTest unit testing" OFF)
+
# OpenGL
option(WITH_GLEW_MX "Support multiple GLEW contexts (experimental)" ON )
@@ -2528,10 +2537,17 @@ endif()
#-----------------------------------------------------------------------------
# Libraries
+if(WITH_GTESTS)
+ include(GTestTesting)
+endif()
+
if(WITH_BLENDER OR WITH_PLAYER)
- add_subdirectory(source)
add_subdirectory(intern)
add_subdirectory(extern)
+
+ # source after intern and extern to gather all
+ # internal and external library information first, for test linking
+ add_subdirectory(source)
elseif(WITH_CYCLES_STANDALONE)
add_subdirectory(intern/cycles)
if(NOT WITH_SYSTEM_GLEW)
@@ -2552,6 +2568,12 @@ if(WITH_PLAYER)
add_subdirectory(source/blenderplayer)
endif()
+
+#-----------------------------------------------------------------------------
+# Testing
+add_subdirectory(tests)
+
+
#-----------------------------------------------------------------------------
# CPack for generating packages
include(build_files/cmake/packaging.cmake)
diff --git a/GNUmakefile b/GNUmakefile
index 4f76cb3565d..c7807720cb9 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -223,7 +223,7 @@ test:
# run pep8 check check on scripts we distribute.
test_pep8:
- python3 source/tests/pep8.py > test_pep8.log 2>&1
+ python3 tests/python/pep8.py > test_pep8.log 2>&1
@echo "written: test_pep8.log"
# run some checks on our cmakefiles.
@@ -233,7 +233,7 @@ test_cmake:
# run deprecation tests, see if we have anything to remove.
test_deprecated:
- python3 source/tests/check_deprecated.py
+ python3 tests/check_deprecated.py
test_style_c:
# run our own checks on C/C++ style
diff --git a/SConstruct b/SConstruct
index 251f7eae92e..0c85fe11cdc 100644
--- a/SConstruct
+++ b/SConstruct
@@ -285,8 +285,7 @@ if env['OURPLATFORM']=='darwin':
import subprocess
command = ["%s"%env['CC'], "--version"]
- process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=None, shell=False)
- line = process.communicate()[0]
+ line = btools.get_command_output(command)
ver = re.search(r'[0-9]+(\.[0-9]+[svn]+)+', line) or re.search(r'[0-9]+(\.[0-9]+)+', line) # read the "based on LLVM x.xsvn" version here, not the Apple version
if ver:
env['CCVERSION'] = ver.group(0).strip('svn')
diff --git a/build_files/buildbot/config/user-config-mac-i386.py b/build_files/buildbot/config/user-config-mac-i386.py
index 296b752649d..07627b91331 100644
--- a/build_files/buildbot/config/user-config-mac-i386.py
+++ b/build_files/buildbot/config/user-config-mac-i386.py
@@ -1,5 +1,5 @@
MACOSX_ARCHITECTURE = 'i386' # valid archs: ppc, i386, ppc64, x86_64
-WITH_BF_CYCLES_CUDA_BINARIES = True
+WITH_BF_CYCLES_CUDA_BINARIES = False
diff --git a/build_files/buildbot/config/user-config-mac-x86_64.py b/build_files/buildbot/config/user-config-mac-x86_64.py
index ac923f48abe..ba42136d7c3 100644
--- a/build_files/buildbot/config/user-config-mac-x86_64.py
+++ b/build_files/buildbot/config/user-config-mac-x86_64.py
@@ -1,5 +1,5 @@
MACOSX_ARCHITECTURE = 'x86_64' # valid archs: ppc, i386, ppc64, x86_64
-WITH_BF_CYCLES_CUDA_BINARIES = True
+WITH_BF_CYCLES_CUDA_BINARIES = False
diff --git a/build_files/cmake/Modules/GTestTesting.cmake b/build_files/cmake/Modules/GTestTesting.cmake
new file mode 100644
index 00000000000..fd0379b8f78
--- /dev/null
+++ b/build_files/cmake/Modules/GTestTesting.cmake
@@ -0,0 +1,47 @@
+#=============================================================================
+# Copyright 2014 Blender Foundation.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#
+# Inspired on the Testing.cmake from Libmv
+#
+#=============================================================================
+
+macro(BLENDER_SRC_GTEST NAME SRC EXTRA_LIBS)
+ if(WITH_GTESTS)
+ get_property(_current_include_directories
+ DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ PROPERTY INCLUDE_DIRECTORIES)
+ set(TEST_INC
+ ${_current_include_directories}
+ ${CMAKE_SOURCE_DIR}/tests/gtests
+ ${CMAKE_SOURCE_DIR}/extern/libmv/third_party/glog/src
+ ${CMAKE_SOURCE_DIR}/extern/libmv/third_party/gflags
+ ${CMAKE_SOURCE_DIR}/extern/gtest/include
+ )
+ unset(_current_include_directories)
+
+ add_executable(${NAME}_test ${SRC})
+ target_link_libraries(${NAME}_test
+ ${EXTRA_LIBS}
+ bf_testing_main
+ bf_intern_guardedalloc
+ extern_gtest
+ extern_glog)
+ set_target_properties(${NAME}_test PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${TESTS_OUTPUT_DIR}"
+ RUNTIME_OUTPUT_DIRECTORY_RELEASE "${TESTS_OUTPUT_DIR}"
+ RUNTIME_OUTPUT_DIRECTORY_DEBUG "${TESTS_OUTPUT_DIR}"
+ INCLUDE_DIRECTORIES "${TEST_INC}")
+ add_test(${NAME}_test ${TESTS_OUTPUT_DIR}/${NAME}_test)
+ endif()
+endmacro()
+
+macro(BLENDER_TEST NAME EXTRA_LIBS)
+ BLENDER_SRC_GTEST("${NAME}" "${NAME}_test.cc" "${EXTRA_LIBS}")
+endmacro()
diff --git a/build_files/cmake/buildinfo.cmake b/build_files/cmake/buildinfo.cmake
index 245d7472158..ead2474fd8a 100644
--- a/build_files/cmake/buildinfo.cmake
+++ b/build_files/cmake/buildinfo.cmake
@@ -86,6 +86,10 @@ if(EXISTS ${SOURCE_DIR}/.git)
endif()
endif()
+ if(MY_WC_BRANCH MATCHES "^blender-v")
+ set(MY_WC_BRANCH "master")
+ endif()
+
unset(_git_below_check)
endif()
diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake
index 80a4858bf94..9c3f46322c1 100644
--- a/build_files/cmake/macros.cmake
+++ b/build_files/cmake/macros.cmake
@@ -415,6 +415,243 @@ macro(setup_liblinks
target_link_libraries(${target} ${PLATFORM_LINKLIBS} ${CMAKE_DL_LIBS})
endmacro()
+macro(SETUP_BLENDER_SORTED_LIBS)
+ get_property(BLENDER_LINK_LIBS GLOBAL PROPERTY BLENDER_LINK_LIBS)
+
+ list(APPEND BLENDER_LINK_LIBS
+ bf_windowmanager
+ bf_render
+ )
+
+ if(WITH_MOD_FLUID)
+ list(APPEND BLENDER_LINK_LIBS bf_intern_elbeem)
+ endif()
+
+ if(WITH_CYCLES)
+ list(APPEND BLENDER_LINK_LIBS
+ cycles_render
+ cycles_bvh
+ cycles_device
+ cycles_kernel
+ cycles_util
+ cycles_subd)
+ if(WITH_CYCLES_OSL)
+ list(APPEND BLENDER_LINK_LIBS cycles_kernel_osl)
+ endif()
+ endif()
+
+ # Sort libraries
+ set(BLENDER_SORTED_LIBS
+ bf_windowmanager
+
+ bf_editor_space_api
+ bf_editor_space_action
+ bf_editor_space_buttons
+ bf_editor_space_console
+ bf_editor_space_file
+ bf_editor_space_graph
+ bf_editor_space_image
+ bf_editor_space_info
+ bf_editor_space_logic
+ bf_editor_space_nla
+ bf_editor_space_node
+ bf_editor_space_outliner
+ bf_editor_space_script
+ bf_editor_space_sequencer
+ bf_editor_space_text
+ bf_editor_space_time
+ bf_editor_space_userpref
+ bf_editor_space_view3d
+ bf_editor_space_clip
+
+ bf_editor_transform
+ bf_editor_util
+ bf_editor_uvedit
+ bf_editor_curve
+ bf_editor_gpencil
+ bf_editor_interface
+ bf_editor_mesh
+ bf_editor_metaball
+ bf_editor_object
+ bf_editor_armature
+ bf_editor_physics
+ bf_editor_render
+ bf_editor_screen
+ bf_editor_sculpt_paint
+ bf_editor_sound
+ bf_editor_animation
+ bf_editor_datafiles
+ bf_editor_mask
+ bf_editor_io
+
+ bf_render
+ bf_python
+ bf_python_ext
+ bf_python_mathutils
+ bf_python_bmesh
+ bf_freestyle
+ bf_ikplugin
+ bf_modifiers
+ bf_bmesh
+ bf_blenkernel
+ bf_nodes
+ bf_gpu
+ bf_blenloader
+ bf_imbuf
+ bf_blenlib
+ bf_intern_ghost
+ bf_intern_string
+ bf_avi
+ bf_imbuf_cineon
+ bf_imbuf_openexr
+ bf_imbuf_openimageio
+ bf_imbuf_dds
+ bf_collada
+ bf_intern_elbeem
+ bf_intern_memutil
+ bf_intern_guardedalloc
+ bf_intern_ctr
+ bf_intern_utfconv
+ ge_blen_routines
+ ge_converter
+ ge_phys_dummy
+ ge_phys_bullet
+ bf_intern_smoke
+ extern_minilzo
+ extern_lzma
+ extern_colamd
+ ge_logic_ketsji
+ extern_recastnavigation
+ ge_logic
+ ge_rasterizer
+ ge_oglrasterizer
+ ge_logic_expressions
+ ge_scenegraph
+ ge_logic_network
+ ge_logic_ngnetwork
+ ge_logic_loopbacknetwork
+ bf_intern_moto
+ extern_openjpeg
+ extern_redcode
+ ge_videotex
+ bf_rna
+ bf_dna
+ bf_blenfont
+ bf_intern_audaspace
+ bf_intern_mikktspace
+ bf_intern_dualcon
+ bf_intern_cycles
+ cycles_render
+ cycles_bvh
+ cycles_device
+ cycles_kernel
+ cycles_util
+ cycles_subd
+ bf_intern_raskter
+ bf_intern_opencolorio
+ extern_rangetree
+ extern_wcwidth
+ extern_libmv
+ extern_glog
+ )
+
+ if(WITH_COMPOSITOR)
+ # added for opencl compositor
+ list_insert_before(BLENDER_SORTED_LIBS "bf_blenkernel" "bf_compositor")
+ list_insert_after(BLENDER_SORTED_LIBS "bf_compositor" "bf_intern_opencl")
+ endif()
+
+ if(WITH_LIBMV)
+ list(APPEND BLENDER_SORTED_LIBS extern_ceres)
+ endif()
+
+ if(WITH_MOD_CLOTH_ELTOPO)
+ list(APPEND BLENDER_SORTED_LIBS extern_eltopo)
+ endif()
+
+ if(NOT WITH_SYSTEM_GLEW)
+ list(APPEND BLENDER_SORTED_LIBS ${BLENDER_GLEW_LIBRARIES})
+ endif()
+
+ if(WITH_BINRELOC)
+ list(APPEND BLENDER_SORTED_LIBS extern_binreloc)
+ endif()
+
+ if(WITH_CXX_GUARDEDALLOC)
+ list(APPEND BLENDER_SORTED_LIBS bf_intern_guardedalloc_cpp)
+ endif()
+
+ if(WITH_IK_SOLVER)
+ list_insert_after(BLENDER_SORTED_LIBS "bf_intern_elbeem" "bf_intern_iksolver")
+ endif()
+
+ if(WITH_IK_ITASC)
+ list(APPEND BLENDER_SORTED_LIBS bf_intern_itasc)
+ endif()
+
+ if(WITH_CODEC_QUICKTIME)
+ list(APPEND BLENDER_SORTED_LIBS bf_quicktime)
+ endif()
+
+ if(WITH_INPUT_NDOF)
+ list(APPEND BLENDER_SORTED_LIBS bf_intern_ghostndof3dconnexion)
+ endif()
+
+ if(WITH_MOD_BOOLEAN)
+ list(APPEND BLENDER_SORTED_LIBS extern_carve)
+ endif()
+
+ if(WITH_GHOST_XDND)
+ list(APPEND BLENDER_SORTED_LIBS extern_xdnd)
+ endif()
+
+ if(WITH_CYCLES_OSL)
+ list_insert_after(BLENDER_SORTED_LIBS "cycles_kernel" "cycles_kernel_osl")
+ endif()
+
+ if(WITH_INTERNATIONAL)
+ list(APPEND BLENDER_SORTED_LIBS bf_intern_locale)
+ endif()
+
+ if(WITH_OPENNL)
+ list_insert_after(BLENDER_SORTED_LIBS "bf_render" "bf_intern_opennl")
+ endif()
+
+ if(WITH_BULLET)
+ list_insert_after(BLENDER_SORTED_LIBS "bf_blenkernel" "bf_intern_rigidbody")
+ endif()
+
+ if(WITH_BULLET AND NOT WITH_SYSTEM_BULLET)
+ list_insert_after(BLENDER_SORTED_LIBS "ge_logic_ngnetwork" "extern_bullet")
+ endif()
+
+ foreach(SORTLIB ${BLENDER_SORTED_LIBS})
+ set(REMLIB ${SORTLIB})
+ foreach(SEARCHLIB ${BLENDER_LINK_LIBS})
+ if(${SEARCHLIB} STREQUAL ${SORTLIB})
+ set(REMLIB "")
+ endif()
+ endforeach()
+ if(REMLIB)
+ # message(STATUS "Removing library ${REMLIB} from blender linking because: not configured")
+ list(APPEND REM_MSG ${REMLIB})
+ list(REMOVE_ITEM BLENDER_SORTED_LIBS ${REMLIB})
+ endif()
+ endforeach()
+ if(REM_MSG)
+ list(SORT REM_MSG)
+ message(STATUS "Blender Skipping: (${REM_MSG})")
+ endif()
+
+ unset(SEARCHLIB)
+ unset(SORTLIB)
+ unset(REMLIB)
+ unset(REM_MSG)
+
+ # for top-level tests
+ set_property(GLOBAL PROPERTY BLENDER_SORTED_LIBS_PROP ${BLENDER_SORTED_LIBS})
+endmacro()
+
macro(TEST_SSE_SUPPORT
_sse_flags
_sse2_flags)
@@ -1008,7 +1245,7 @@ macro(msgfmt_simple
OUTPUT ${_file_to}
COMMAND ${CMAKE_COMMAND} -E make_directory ${_file_to_path}
COMMAND ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/msgfmt ${_file_from} ${_file_to}
- DEPENDS msgfmt)
+ DEPENDS msgfmt ${_file_from})
set_source_files_properties(${_file_to} PROPERTIES GENERATED TRUE)
diff --git a/build_files/scons/config/win32-vc-config.py b/build_files/scons/config/win32-vc-config.py
index 524446d4b2e..ebe24bf0644 100644
--- a/build_files/scons/config/win32-vc-config.py
+++ b/build_files/scons/config/win32-vc-config.py
@@ -31,7 +31,7 @@ BF_PYTHON_LIB = 'python${BF_PYTHON_VERSION[0]}${BF_PYTHON_VERSION[2]}'
BF_PYTHON_DLL = '${BF_PYTHON_LIB}'
BF_PYTHON_LIBPATH = '${BF_PYTHON}/lib'
-WITH_BF_PYTHON_INSTALL_NUMPY = False
+WITH_BF_PYTHON_INSTALL_NUMPY = True
WITH_BF_OPENAL = True
BF_OPENAL = LIBDIR + '/openal'
diff --git a/build_files/scons/config/win64-vc-config.py b/build_files/scons/config/win64-vc-config.py
index a45c0b1d3b6..d94de3b54e4 100644
--- a/build_files/scons/config/win64-vc-config.py
+++ b/build_files/scons/config/win64-vc-config.py
@@ -32,7 +32,7 @@ BF_PYTHON_LIB = 'python${BF_PYTHON_VERSION[0]}${BF_PYTHON_VERSION[2]}'
BF_PYTHON_DLL = '${BF_PYTHON_LIB}'
BF_PYTHON_LIBPATH = '${BF_PYTHON}/lib'
-WITH_BF_PYTHON_INSTALL_NUMPY = False
+WITH_BF_PYTHON_INSTALL_NUMPY = True
WITH_BF_OPENAL = True
BF_OPENAL = LIBDIR + '/openal'
diff --git a/build_files/scons/tools/Blender.py b/build_files/scons/tools/Blender.py
index 621a85a2d91..e0f18480c74 100644
--- a/build_files/scons/tools/Blender.py
+++ b/build_files/scons/tools/Blender.py
@@ -406,57 +406,66 @@ def buildinfo(lenv, build_type):
"""
Generate a buildinfo object
"""
+ import subprocess
+
build_date = time.strftime ("%Y-%m-%d")
build_time = time.strftime ("%H:%M:%S")
if os.path.isdir(os.path.abspath('.git')):
- build_commit_timestamp = os.popen('git log -1 --format=%ct').read().strip()
+ try:
+ build_commit_timestamp = btools.get_command_output(args=['git', 'log', '-1', '--format=%ct']).strip()
+ except OSError:
+ build_commit_timestamp = None
if not build_commit_timestamp:
# Git command not found
build_hash = 'unknown'
build_commit_timestamp = '0'
build_branch = 'unknown'
else:
- import subprocess
no_upstream = False
- process = subprocess.Popen(['git', 'rev-parse', '--short', '@{u}'],
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- build_hash, stderr = process.communicate()
- build_hash = build_hash.strip()
- build_branch = os.popen('git rev-parse --abbrev-ref HEAD').read().strip()
+ try :
+ build_hash = btools.get_command_output(['git', 'rev-parse', '--short', '@{u}']).strip()
+ except subprocess.CalledProcessError:
+ # assume branch has no upstream configured
+ build_hash = ''
+
+ build_branch = btools.get_command_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).strip()
if build_branch == 'HEAD':
- master_check = os.popen('git branch --list master --contains ' + build_hash).read().strip()
+ master_check = btools.get_command_output(['git', 'branch', '--list', 'master', '--contains', build_hash]).strip()
if master_check == 'master':
build_branch = 'master'
else:
- head_hash = os.popen('git rev-parse HEAD').read().strip()
- tag_hashes = os.popen('git show-ref --tags -d').read()
+ head_hash = btools.get_command_output(['git', 'rev-parse', 'HEAD']).strip()
+ tag_hashes = btools.get_command_output(['git', 'show-ref', '--tags', '-d'])
if tag_hashes.find(head_hash) != -1:
build_branch = 'master'
if build_hash == '':
- build_hash = os.popen('git rev-parse --short HEAD').read().strip()
+ build_hash = btools.get_command_output(['git', 'rev-parse', '--short', 'HEAD']).strip()
no_upstream = True
else:
- older_commits = os.popen('git log --oneline HEAD..@{u}').read().strip()
+ older_commits = btools.get_command_output(['git', 'log', '--oneline', 'HEAD..@{u}']).strip()
if older_commits:
- build_hash = os.popen('git rev-parse --short HEAD').read().strip()
+ build_hash = btools.get_command_output(['git', 'rev-parse', '--short', 'HEAD']).strip()
# ## Check for local modifications
has_local_changes = False
# Update GIT index before getting dirty files
os.system('git update-index -q --refresh')
- changed_files = os.popen('git diff-index --name-only HEAD --').read().strip()
+ changed_files = btools.get_command_output(['git', 'diff-index', '--name-only', 'HEAD', '--']).strip()
if changed_files:
has_local_changes = True
elif no_upstream == False:
- unpushed_log = os.popen('git log --oneline @{u}..').read().strip()
+ unpushed_log = btools.get_command_output(['git', 'log', '--oneline', '@{u}..']).strip()
has_local_changes = unpushed_log != ''
+ if build_branch.startswith('blender-v'):
+ build_branch = 'master'
+
if has_local_changes:
build_branch += ' (modified)'
else:
diff --git a/build_files/scons/tools/btools.py b/build_files/scons/tools/btools.py
index 6eff3dd682b..4124d066734 100644
--- a/build_files/scons/tools/btools.py
+++ b/build_files/scons/tools/btools.py
@@ -14,6 +14,21 @@ import sys
Variables = SCons.Variables
BoolVariable = SCons.Variables.BoolVariable
+def get_command_output(*popenargs, **kwargs):
+ if hasattr(subprocess, "check_output"):
+ return subprocess.check_output(*popenargs, **kwargs)
+ if 'stdout' in kwargs:
+ raise ValueError('stdout argument not allowed, it will be overridden.')
+ process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
+ output, unused_err = process.communicate()
+ retcode = process.poll()
+ if retcode:
+ cmd = kwargs.get("args")
+ if cmd is None:
+ cmd = popenargs[0]
+ raise subprocess.CalledProcessError(retcode, cmd)
+ return output
+
def get_version():
import re
@@ -56,7 +71,11 @@ def get_version():
raise Exception("%s: missing version string" % fname)
def get_hash():
- build_hash = os.popen('git rev-parse --short HEAD').read().strip()
+ try:
+ build_hash = get_command_output(['git', 'rev-parse', '--short', 'HEAD']).strip()
+ except OSError:
+ build_hash = None
+ print("WARNING: could not use git to retrieve current Blender repository hash...")
if build_hash == '' or build_hash == None:
build_hash = 'UNKNOWN'
diff --git a/doc/manpage/blender.1 b/doc/manpage/blender.1
deleted file mode 100644
index b7d96dcbb3b..00000000000
--- a/doc/manpage/blender.1
+++ /dev/null
@@ -1,457 +0,0 @@
-.TH "BLENDER" "1" "March 6, 2014" "Blender Blender 2\&.70"
-
-.SH NAME
-blender \- a 3D modelling and rendering package
-.SH SYNOPSIS
-.B blender [args ...] [file] [args ...]
-.br
-.SH DESCRIPTION
-.PP
-.B blender
-is a 3D modelling and rendering package. It is the in-house software of a high quality animation studio, Blender has proven to be an extremely fast and versatile design instrument. The software has a personal touch, offering a unique approach to the world of Three Dimensions.
-
-Use Blender to create TV commercials, to make technical visualizations, business graphics, to do some morphing, or design user interfaces. You can easy build and manage complex environments. The renderer is versatile and extremely fast. All basic animation principles (curves & keys) are well implemented.
-
-http://www.blender.org
-.SH OPTIONS
-
-Blender 2.66
-Usage: blender [args ...] [file] [args ...]
-.br
-.SS "Render Options:"
-
-.TP
-.B \-b or \-\-background <file>
-.br
-Load <file> in background (often used for UI\-less rendering)
-.br
-
-.TP
-.B \-a or \-\-render\-anim
-.br
-Render frames from start to end (inclusive)
-.br
-
-.TP
-.B \-S or \-\-scene <name>
-.br
-Set the active scene <name> for rendering
-.br
-
-.TP
-.B \-f or \-\-render\-frame <frame>
-.br
-Render frame <frame> and save it.
-.br
-+<frame> start frame relative, \-<frame> end frame relative.
-.br
-
-.TP
-.B \-s or \-\-frame\-start <frame>
-.br
-Set start to frame <frame> (use before the \-a argument)
-.br
-
-.TP
-.B \-e or \-\-frame\-end <frame>
-.br
-Set end to frame <frame> (use before the \-a argument)
-.br
-
-.TP
-.B \-j or \-\-frame\-jump <frames>
-.br
-Set number of frames to step forward after each rendered frame
-.br
-
-.TP
-.B \-o or \-\-render\-output <path>
-.br
-Set the render path and file name.
-.br
-Use // at the start of the path to
-.br
- render relative to the blend file.
-.br
-The # characters are replaced by the frame number, and used to define zero padding.
-.br
- ani_##_test.png becomes ani_01_test.png
-.br
- test\-######.png becomes test\-000001.png
-.br
- When the filename does not contain #, The suffix #### is added to the filename
-.br
-The frame number will be added at the end of the filename.
-.br
- eg: blender \-b foobar.blend \-o //render_ \-F PNG \-x 1 \-a
-.br
- //render_ becomes //render_####, writing frames as //render_0001.png//
-.br
-
-.TP
-.B \-E or \-\-engine <engine>
-.br
-Specify the render engine
-.br
-use \-E help to list available engines
-.br
-
-.IP
-
-.SS "Format Options:"
-
-.TP
-.B \-F or \-\-render\-format <format>
-.br
-Set the render format, Valid options are...
-.br
- TGA IRIS JPEG MOVIE IRIZ RAWTGA
-.br
- AVIRAW AVIJPEG PNG BMP FRAMESERVER
-.br
-(formats that can be compiled into blender, not available on all systems)
-.br
- HDR TIFF EXR MULTILAYER MPEG AVICODEC QUICKTIME CINEON DPX DDS
-.br
-
-.TP
-.B \-x or \-\-use\-extension <bool>
-.br
-Set option to add the file extension to the end of the file
-.br
-
-.TP
-.B \-t or \-\-threads <threads>
-.br
-Use amount of <threads> for rendering in background
-.br
-[1\-64], 0 for systems processor count.
-.br
-
-.IP
-
-.SS "Animation Playback Options:"
-
-.TP
-.B \-a <options> <file(s)>
-.br
-Playback <file(s)>, only operates this way when not running in background.
-.br
- \-p <sx> <sy> Open with lower left corner at <sx>, <sy>
-.br
- \-m Read from disk (Don't buffer)
-.br
- \-f <fps> <fps\-base> Specify FPS to start with
-.br
- \-j <frame> Set frame step to <frame>
-.br
- \-s <frame> Play from <frame>
-.br
- \-e <frame> Play until <frame>
-.br
-
-.IP
-
-.SS "Window Options:"
-
-.TP
-.B \-w or \-\-window\-border
-.br
-Force opening with borders (default)
-.br
-
-.TP
-.B \-W or \-\-window\-borderless
-.br
-Force opening without borders
-.br
-
-.TP
-.B \-p or \-\-window\-geometry <sx> <sy> <w> <h>
-.br
-Open with lower left corner at <sx>, <sy> and width and height as <w>, <h>
-.br
-
-.TP
-.B \-con or \-\-start\-console
-.br
-Start with the console window open (ignored if \-b is set)
-.br
-
-.IP
-
-.SS "Game Engine Specific Options:"
-
-.TP
-.B \-g Game Engine specific options
-.br
-\-g fixedtime Run on 50 hertz without dropping frames
-.br
-\-g vertexarrays Use Vertex Arrays for rendering (usually faster)
-.br
-\-g nomipmap No Texture Mipmapping
-.br
-\-g linearmipmap Linear Texture Mipmapping instead of Nearest (default)
-.br
-
-.IP
-
-.SS "Misc Options:"
-
-.TP
-.B \-d or \-\-debug
-.br
-Turn debugging on
-.br
-
-.IP
-* Prints every operator call and their arguments
-.br
-* Disables mouse grab (to interact with a debugger in some cases)
-.br
-* Keeps python sys.stdin rather than setting it to None
-.br
-
-.TP
-.B \-\-debug\-fpe
-.br
-Enable floating point exceptions
-.br
-
-.TP
-.B \-\-disable\-crash\-handler
-.br
-Disable the crash handler
-.br
-
-.IP
-
-.TP
-.B \-\-factory\-startup
-.br
-Skip reading the "startup.blend" in the users home directory
-.br
-
-.IP
-
-.TP
-.B \-\-env\-system\-datafiles
-.br
-Set the BLENDER_SYSTEM_DATAFILES environment variable
-.br
-
-.TP
-.B \-\-env\-system\-scripts
-.br
-Set the BLENDER_SYSTEM_SCRIPTS environment variable
-.br
-
-.TP
-.B \-\-env\-system\-python
-.br
-Set the BLENDER_SYSTEM_PYTHON environment variable
-.br
-
-.IP
-
-.TP
-.B \-nojoystick
-.br
-Disable joystick support
-.br
-
-.TP
-.B \-noglsl
-.br
-Disable GLSL shading
-.br
-
-.TP
-.B \-noaudio
-.br
-Force sound system to None
-.br
-
-.TP
-.B \-setaudio
-.br
-Force sound system to a specific device
-.br
-NULL SDL OPENAL JACK
-.br
-
-.IP
-
-.TP
-.B \-h or \-\-help
-.br
-Print this help text and exit
-.br
-
-.IP
-
-.TP
-.B \-y or \-\-enable\-autoexec
-.br
-Enable automatic python script execution, (default)
-.br
-
-.TP
-.B \-Y or \-\-disable\-autoexec
-.br
-Disable automatic python script execution (pydrivers & startup scripts)
-.br
-
-.IP
-
-.TP
-.B \-P or \-\-python <filename>
-.br
-Run the given Python script file
-.br
-
-.TP
-.B \-\-python\-text <name>
-.br
-Run the given Python script text block
-.br
-
-.TP
-.B \-\-python\-console
-.br
-Run blender with an interactive console
-.br
-
-.TP
-.B \-\-addons
-.br
-Comma separated list of addons (no spaces)
-.br
-
-.TP
-.B \-v or \-\-version
-.br
-Print Blender version and exit
-.br
-
-.TP
-.B \-\-
-.br
-Ends option processing, following arguments passed unchanged. Access via python's sys.argv
-.br
-
-.SS "Other Options:"
-
-.TP
-.B /?
-.br
-Print this help text and exit (windows only)
-.br
-
-.TP
-.B \-\-debug\-python
-.br
-Enable debug messages for python
-.br
-
-.TP
-.B \-\-debug\-events
-.br
-Enable debug messages for the event system
-.br
-
-.TP
-.B \-\-debug\-handlers
-.br
-Enable debug messages for event handling
-.br
-
-.TP
-.B \-\-debug\-wm
-.br
-Enable debug messages for the window manager
-.br
-
-.TP
-.B \-\-debug\-all
-.br
-Enable all debug messages (excludes libmv)
-.br
-
-.TP
-.B \-\-debug\-value <value>
-.br
-Set debug value of <value> on startup
-.br
-
-.IP
-
-.TP
-.B \-\-debug\-jobs
-.br
-Enable time profiling for background jobs.
-.br
-
-.TP
-.B \-\-verbose <verbose>
-.br
-Set logging verbosity level.
-.br
-
-.TP
-.B \-R
-.br
-Register .blend extension, then exit (Windows only)
-.br
-
-.TP
-.B \-r
-.br
-Silently register .blend extension, then exit (Windows only)
-.br
-
-.TP
-.B \-\-no\-native\-pixels
-.br
-Do not use native pixel size, for high resolution displays (MacBook 'Retina')
-.br
-
-.SS "Argument Parsing:"
-
- arguments must be separated by white space. eg
- "blender \-ba test.blend"
- ...will ignore the 'a'
- "blender \-b test.blend \-f8"
- ...will ignore 8 because there is no space between the \-f and the frame value
-.br
-.SS "Argument Order:"
-
-Arguments are executed in the order they are given. eg
- "blender \-\-background test.blend \-\-render\-frame 1 \-\-render\-output /tmp"
- ...will not render to /tmp because '\-\-render\-frame 1' renders before the output path is set
- "blender \-\-background \-\-render\-output /tmp test.blend \-\-render\-frame 1"
- ...will not render to /tmp because loading the blend file overwrites the render output that was set
- "blender \-\-background test.blend \-\-render\-output /tmp \-\-render\-frame 1" works as expected.
-.br
-.br
-.SH "ENVIRONMENT VARIABLES"
- \fIBLENDER_USER_CONFIG\fR Directory for user configuration files.
- \fIBLENDER_USER_SCRIPTS\fR Directory for user scripts.
- \fIBLENDER_SYSTEM_SCRIPTS\fR Directory for system wide scripts.
- \fIDirectory\fR for user data files (icons, translations, ..).
- \fIBLENDER_SYSTEM_DATAFILES\fR Directory for system wide data files.
- \fIBLENDER_SYSTEM_PYTHON\fR Directory for system python libraries.
- \fITMP\fR or \fITMPDIR\fR Store temporary files here.
- \fIPYTHONHOME\fR Path to the python directory, eg. /usr/lib/python.
-.br
-.br
-
-.br
-.SH SEE ALSO
-.B yafaray(1)
-
-.br
-.SH AUTHORS
-This manpage was written for a Debian GNU/Linux system by Daniel Mester
-<mester@uni-bremen.de> and updated by Cyril Brulebois
-<cyril.brulebois@enst-bretagne.fr> and Dan Eicher <dan@trollwerks.org>.
diff --git a/doc/manpage/blender.1.py b/doc/manpage/blender.1.py
index 646b2aa374b..32e8cc06857 100644..100755
--- a/doc/manpage/blender.1.py
+++ b/doc/manpage/blender.1.py
@@ -18,6 +18,16 @@
#
# ##### END GPL LICENSE BLOCK #####
+'''
+This script generates the blender.1 man page, embedding the help text
+from the Blender executable itself. Invoke it as follows:
+
+ blender.1.py <path-to-blender> <output-filename>
+
+where <path-to-blender> is the path to the Blender executable,
+and <output-filename> is where to write the generated man page.
+'''
+
# <pep8 compliant>
import subprocess
@@ -33,26 +43,23 @@ def man_format(data):
data = data.replace("\t", " ")
return data
-# allow passing blender as argument
-if sys.argv[-1].endswith(os.sep + "blender"):
- blender_bin = sys.argv[-1]
-else:
- blender_bin = os.path.join(os.path.dirname(__file__), "../../blender.bin")
+if len(sys.argv) != 3:
+ import getopt
+ raise getopt.GetoptError("Usage: %s <path-to-blender> <output-filename>" % sys.argv[0])
+
+blender_bin = sys.argv[1]
+outfilename = sys.argv[2]
cmd = [blender_bin, "--help"]
print(" executing:", " ".join(cmd))
-blender_help = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0].decode(encoding="utf-8")
-
-blender_version = subprocess.Popen([blender_bin, "--version"], stdout=subprocess.PIPE).communicate()[0].decode(encoding="utf-8").strip()
-blender_version = blender_version.split("Build")[0]
-
+blender_help = subprocess.check_output(cmd).decode(encoding="utf-8")
+blender_version = subprocess.check_output([blender_bin, "--version"]).decode(encoding="utf-8").strip()
+blender_version = blender_version.split("build")[0].rstrip()
+blender_version = blender_version.partition(" ")[2] # remove 'Blender' prefix.
date_string = datetime.date.fromtimestamp(time.time()).strftime("%B %d, %Y")
-filepath = os.path.splitext(__file__)[0]
-
-file = open(filepath, "w")
-
-fw = file.write
+outfile = open(outfilename, "w")
+fw = outfile.write
fw('.TH "BLENDER" "1" "%s" "Blender %s"\n' % (date_string, blender_version.replace(".", "\\&.")))
@@ -128,4 +135,5 @@ This manpage was written for a Debian GNU/Linux system by Daniel Mester
<cyril.brulebois@enst-bretagne.fr> and Dan Eicher <dan@trollwerks.org>.
''')
-print("written:", filepath)
+outfile.close()
+print("written:", outfilename)
diff --git a/doc/python_api/examples/mathutils.Euler.py b/doc/python_api/examples/mathutils.Euler.py
index 3f87cc0ab04..bfd2a3ed5a0 100644
--- a/doc/python_api/examples/mathutils.Euler.py
+++ b/doc/python_api/examples/mathutils.Euler.py
@@ -5,7 +5,7 @@ import math
eul = mathutils.Euler((0.0, math.radians(45.0), 0.0), 'XYZ')
# rotate the euler
-eul.rotate_axis(math.radians(10.0), 'Z')
+eul.rotate_axis('Z', math.radians(10.0))
# you can access its components by attribute or index
print("Euler X", eul.x)
diff --git a/doc/python_api/rst/bge.logic.rst b/doc/python_api/rst/bge.logic.rst
index 0ddae476e08..4e0d317e4bf 100644
--- a/doc/python_api/rst/bge.logic.rst
+++ b/doc/python_api/rst/bge.logic.rst
@@ -72,6 +72,7 @@ See the actuator's reference for available methods
* :class:`~bge.types.KX_CameraActuator`
* :class:`~bge.types.KX_ConstraintActuator`
* :class:`~bge.types.KX_GameActuator`
+ * :class:`~bge.types.KX_MouseActuator`
* :class:`~bge.types.KX_NetworkMessageActuator`
* :class:`~bge.types.KX_ObjectActuator`
* :class:`~bge.types.KX_ParentActuator`
@@ -507,6 +508,18 @@ Property Sensor
:value: 5
+.. data:: KX_PROPSENSOR_LESSTHAN
+
+ Activate when the property is less than the sensor value
+
+ :value: 6
+
+.. data:: KX_PROPSENSOR_GREATERTHAN
+
+ Activate when the property is greater than the sensor value
+
+ :value: 7
+
------------
Radar Sensor
------------
@@ -752,6 +765,16 @@ See :class:`bge.types.KX_GameActuator`
.. data:: KX_GAME_SAVECFG
.. data:: KX_GAME_LOADCFG
+.. _mouse-actuator:
+
+---------------
+Mouse Actuator
+---------------
+
+.. data:: KX_ACT_MOUSE_OBJECT_AXIS_X
+.. data:: KX_ACT_MOUSE_OBJECT_AXIS_Y
+.. data:: KX_ACT_MOUSE_OBJECT_AXIS_Z
+
---------------
Parent Actuator
---------------
diff --git a/doc/python_api/rst/bge_types/bge.types.KX_MouseActuator.rst b/doc/python_api/rst/bge_types/bge.types.KX_MouseActuator.rst
new file mode 100644
index 00000000000..cc3ce49d0a5
--- /dev/null
+++ b/doc/python_api/rst/bge_types/bge.types.KX_MouseActuator.rst
@@ -0,0 +1,103 @@
+KX_MouseActuator(SCA_IActuator)
+====================================
+
+.. module:: bge.types
+
+base class --- :class:`SCA_IActuator`
+
+.. class:: KX_MouseActuator(SCA_IActuator)
+
+ The mouse actuator gives control over the visibility of the mouse cursor and rotates the parent object according to mouse movement.
+
+ .. method:: reset()
+
+ Undoes the rotation caused by the mouse actuator.
+
+ .. attribute:: visible
+
+ The visibility of the mouse cursor.
+
+ :type: boolean
+
+ .. attribute:: use_axis_x
+
+ Mouse movement along the x axis effects object rotation.
+
+ :type: boolean
+
+ .. attribute:: use_axis_y
+
+ Mouse movement along the y axis effects object rotation.
+
+ :type: boolean
+
+ .. attribute:: threshold
+
+ Amount of movement from the mouse required before rotation is triggered.
+
+ :type: list (vector of 2 floats)
+
+ The values in the list should be between 0.0 and 0.5.
+
+ .. attribute:: reset_x
+
+ Mouse is locked to the center of the screen on the x axis.
+
+ :type: boolean
+
+ .. attribute:: reset_y
+
+ Mouse is locked to the center of the screen on the y axis.
+
+ :type: boolean
+
+ .. attribute:: object_axis
+
+ The object's 3D axis to rotate with the mouse movement. ([x, y])
+
+ :type: list (vector of 2 integers from 0 to 2)
+
+ * KX_ACT_MOUSE_OBJECT_AXIS_X
+ * KX_ACT_MOUSE_OBJECT_AXIS_Y
+ * KX_ACT_MOUSE_OBJECT_AXIS_Z
+
+ .. attribute:: local_x
+
+ Rotation caused by mouse movement along the x axis is local.
+
+ :type: boolean
+
+ .. attribute:: local_y
+
+ Rotation caused by mouse movement along the y axis is local.
+
+ :type: boolean
+
+ .. attribute:: sensitivity
+
+ The amount of rotation caused by mouse movement along the x and y axis.
+
+ :type: list (vector of 2 floats)
+
+ Negative values invert the rotation.
+
+ .. attribute:: limit_x
+
+ The minimum and maximum angle of rotation caused by mouse movement along the x axis in degrees.
+ limit_x[0] is minimum, limit_x[1] is maximum.
+
+ :type: list (vector of 2 floats)
+
+ .. attribute:: limit_y
+
+ The minimum and maximum angle of rotation caused by mouse movement along the y axis in degrees.
+ limit_y[0] is minimum, limit_y[1] is maximum.
+
+ :type: list (vector of 2 floats)
+
+ .. attribute:: angle
+
+ The current rotational offset caused by the mouse actuator in degrees.
+
+ :type: list (vector of 2 floats)
+
diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt
index c0b6a0bda49..ca7c5ff6f71 100644
--- a/extern/CMakeLists.txt
+++ b/extern/CMakeLists.txt
@@ -86,3 +86,7 @@ if(WITH_GHOST_XDND)
add_subdirectory(xdnd)
endif()
endif()
+
+if(WITH_GTESTS)
+ add_subdirectory(gtest)
+endif()
diff --git a/extern/carve/carve-capi.cc b/extern/carve/carve-capi.cc
index af9ecad685d..d8c7727605c 100644
--- a/extern/carve/carve-capi.cc
+++ b/extern/carve/carve-capi.cc
@@ -48,8 +48,8 @@ typedef struct CarveMeshDescr {
// N-th element of the vector indicates index of an original mesh loop.
std::unordered_map<std::pair<int, int>, int> orig_loop_index_map;
- // N-th element of the vector indicates index of an original mesh poly.
- std::vector<int> orig_poly_index_map;
+ // Mapping from carve face to an original face index in DM.
+ std::unordered_map<const MeshSet<3>::face_t *, int> orig_poly_index_map;
// The folloving mapping is only filled in for output mesh.
@@ -150,7 +150,7 @@ inline int indexOf(const T *element, const std::vector<T> &vector_from)
void initOrigIndexMeshFaceMapping(CarveMeshDescr *mesh,
int which_mesh,
std::unordered_map<std::pair<int, int>, int> &orig_loop_index_map,
- const std::vector<int> &orig_poly_index_map,
+ std::unordered_map<const MeshSet<3>::face_t*, int> &orig_poly_index_map,
OrigVertMapping *orig_vert_mapping,
OrigFaceEdgeMapping *orig_face_edge_mapping,
FaceEdgeTriangulatedFlag *face_edge_triangulated_flag,
@@ -177,7 +177,7 @@ void initOrigIndexMeshFaceMapping(CarveMeshDescr *mesh,
const MeshSet<3>::face_t *face = *face_iter;
// Mapping from carve face back to original poly index.
- int orig_poly_index = orig_poly_index_map[i];
+ int orig_poly_index = orig_poly_index_map[face];
orig_face_attr->setAttribute(face, std::make_pair(which_mesh, orig_poly_index));
for (MeshSet<3>::face_t::const_edge_iter_t edge_iter = face->begin();
@@ -566,14 +566,14 @@ CarveMeshDescr *carve_addMesh(struct ImportMeshData *import_data,
// Import verices from external mesh to Carve.
int num_verts = mesh_importer->getNumVerts(import_data);
- std::vector<carve::geom3d::Vector> vertices;
- vertices.reserve(num_verts);
+ std::vector<MeshSet<3>::vertex_t> vertex_storage;
+ vertex_storage.reserve(num_verts);
for (int i = 0; i < num_verts; i++) {
float position[3];
mesh_importer->getVertCoord(import_data, i, position);
- vertices.push_back(carve::geom::VECTOR(position[0],
- position[1],
- position[2]));
+ vertex_storage.push_back(carve::geom::VECTOR(position[0],
+ position[1],
+ position[2]));
}
// Import polys from external mesh to Carve.
@@ -581,14 +581,13 @@ CarveMeshDescr *carve_addMesh(struct ImportMeshData *import_data,
int *verts_of_poly_dynamic = NULL;
int verts_of_poly_dynamic_size = 0;
- int num_loops = mesh_importer->getNumLoops(import_data);
int num_polys = mesh_importer->getNumPolys(import_data);
int loop_index = 0;
- int num_tessellated_polys = 0;
std::vector<int> face_indices;
- face_indices.reserve(num_loops);
- mesh_descr->orig_poly_index_map.reserve(num_polys);
TrianglesStorage triangles_storage;
+ std::vector<MeshSet<3>::face_t *> faces;
+ std::vector<MeshSet<3>::vertex_t *> face_vertices;
+ faces.reserve(num_polys);
for (int i = 0; i < num_polys; i++) {
int verts_per_poly =
mesh_importer->getNumPolyVerts(import_data, i);
@@ -611,32 +610,39 @@ CarveMeshDescr *carve_addMesh(struct ImportMeshData *import_data,
mesh_importer->getPolyVerts(import_data, i, verts_of_poly);
carve::math::Matrix3 axis_matrix;
- if (!carve_checkPolyPlanarAndGetNormal(vertices,
+ if (!carve_checkPolyPlanarAndGetNormal(vertex_storage,
verts_per_poly,
verts_of_poly,
&axis_matrix)) {
+ face_indices.clear();
int num_triangles = carve_triangulatePoly(import_data,
mesh_importer,
- vertices,
+ vertex_storage,
verts_per_poly,
verts_of_poly,
axis_matrix,
&face_indices,
&triangles_storage);
-
for (int j = 0; j < num_triangles; ++j) {
- mesh_descr->orig_poly_index_map.push_back(i);
+ MeshSet<3>::face_t *face = new MeshSet<3>::face_t(
+ &vertex_storage[face_indices[j * 3]],
+ &vertex_storage[face_indices[j * 3 + 1]],
+ &vertex_storage[face_indices[j * 3 + 2]]);
+ mesh_descr->orig_poly_index_map[face] = i;
+ faces.push_back(face);
}
-
- num_tessellated_polys += num_triangles;
}
else {
- face_indices.push_back(verts_per_poly);
+ face_vertices.clear();
+ face_vertices.reserve(verts_per_poly);
for (int j = 0; j < verts_per_poly; ++j) {
- face_indices.push_back(verts_of_poly[j]);
+ face_vertices.push_back(&vertex_storage[verts_of_poly[j]]);
}
- mesh_descr->orig_poly_index_map.push_back(i);
- num_tessellated_polys++;
+ MeshSet<3>::face_t *face =
+ new MeshSet<3>::face_t(face_vertices.begin(),
+ face_vertices.end());
+ mesh_descr->orig_poly_index_map[face] = i;
+ faces.push_back(face);
}
for (int j = 0; j < verts_per_poly; ++j) {
@@ -650,9 +656,9 @@ CarveMeshDescr *carve_addMesh(struct ImportMeshData *import_data,
delete [] verts_of_poly_dynamic;
}
- mesh_descr->poly = new MeshSet<3> (vertices,
- num_tessellated_polys,
- face_indices);
+ std::vector<MeshSet<3>::mesh_t *> meshes;
+ MeshSet<3>::mesh_t::create(faces.begin(), faces.end(), meshes, carve::mesh::MeshOptions());
+ mesh_descr->poly = new MeshSet<3> (vertex_storage, meshes);
return mesh_descr;
diff --git a/extern/carve/carve-util.cc b/extern/carve/carve-util.cc
index d02b786fd2a..1106fa16a21 100644
--- a/extern/carve/carve-util.cc
+++ b/extern/carve/carve-util.cc
@@ -498,7 +498,7 @@ static inline void add_newell_cross_v3_v3v3(const Vector &v_prev,
}
// Axis matrix is being set for non-flat ngons only.
-bool carve_checkPolyPlanarAndGetNormal(const std::vector<Vector> &vertices,
+bool carve_checkPolyPlanarAndGetNormal(const std::vector<MeshSet<3>::vertex_t> &vertex_storage,
const int verts_per_poly,
const int *verts_of_poly,
Matrix3 *axis_matrix_r)
@@ -510,10 +510,10 @@ bool carve_checkPolyPlanarAndGetNormal(const std::vector<Vector> &vertices,
else if (verts_per_poly == 4) {
// Presumably faster than using generig n-gon check for quads.
- const Vector &v1 = vertices[verts_of_poly[0]],
- &v2 = vertices[verts_of_poly[1]],
- &v3 = vertices[verts_of_poly[2]],
- &v4 = vertices[verts_of_poly[3]];
+ const Vector &v1 = vertex_storage[verts_of_poly[0]].v,
+ &v2 = vertex_storage[verts_of_poly[1]].v,
+ &v3 = vertex_storage[verts_of_poly[2]].v,
+ &v4 = vertex_storage[verts_of_poly[3]].v;
Vector vec1, vec2, vec3, cross;
@@ -532,14 +532,14 @@ bool carve_checkPolyPlanarAndGetNormal(const std::vector<Vector> &vertices,
return fabs(production) < magnitude;
}
else {
- const Vector *vert_prev = &vertices[verts_of_poly[verts_per_poly - 1]];
- const Vector *vert_curr = &vertices[verts_of_poly[0]];
+ const Vector *vert_prev = &vertex_storage[verts_of_poly[verts_per_poly - 1]].v;
+ const Vector *vert_curr = &vertex_storage[verts_of_poly[0]].v;
Vector normal = carve::geom::VECTOR(0.0, 0.0, 0.0);
for (int i = 0; i < verts_per_poly; i++) {
add_newell_cross_v3_v3v3(*vert_prev, *vert_curr, &normal);
vert_prev = vert_curr;
- vert_curr = &vertices[verts_of_poly[(i + 1) % verts_per_poly]];
+ vert_curr = &vertex_storage[verts_of_poly[(i + 1) % verts_per_poly]].v;
}
if (normal.length2() < FLT_EPSILON) {
@@ -552,11 +552,11 @@ bool carve_checkPolyPlanarAndGetNormal(const std::vector<Vector> &vertices,
normal.normalize();
axis_dominant_v3_to_m3__bli(axis_matrix_r, normal);
- Vector first_projected = *axis_matrix_r * vertices[verts_of_poly[0]];
+ Vector first_projected = *axis_matrix_r * vertex_storage[verts_of_poly[0]].v;
double min_z = first_projected[2], max_z = first_projected[2];
for (int i = 1; i < verts_per_poly; i++) {
- const Vector &vertex = vertices[verts_of_poly[i]];
+ const Vector &vertex = vertex_storage[verts_of_poly[i]].v;
Vector projected = *axis_matrix_r * vertex;
if (projected[2] < min_z) {
min_z = projected[2];
@@ -579,7 +579,7 @@ bool carve_checkPolyPlanarAndGetNormal(const std::vector<Vector> &vertices,
namespace {
-int triangulateNGon_carveTriangulator(const std::vector<Vector> &vertices,
+int triangulateNGon_carveTriangulator(const std::vector<MeshSet<3>::vertex_t> &vertex_storage,
const int verts_per_poly,
const int *verts_of_poly,
const Matrix3 &axis_matrix,
@@ -590,7 +590,7 @@ int triangulateNGon_carveTriangulator(const std::vector<Vector> &vertices,
std::vector<carve::geom::vector<2> > poly_2d;
poly_2d.reserve(verts_per_poly);
for (int i = 0; i < verts_per_poly; ++i) {
- projected = axis_matrix * vertices[verts_of_poly[i]];
+ projected = axis_matrix * vertex_storage[verts_of_poly[i]].v;
poly_2d.push_back(carve::geom::VECTOR(projected[0], projected[1]));
}
@@ -602,7 +602,7 @@ int triangulateNGon_carveTriangulator(const std::vector<Vector> &vertices,
int triangulateNGon_importerTriangulator(struct ImportMeshData *import_data,
CarveMeshImporter *mesh_importer,
- const std::vector<Vector> &vertices,
+ const std::vector<MeshSet<3>::vertex_t> &vertex_storage,
const int verts_per_poly,
const int *verts_of_poly,
const Matrix3 &axis_matrix,
@@ -615,7 +615,7 @@ int triangulateNGon_importerTriangulator(struct ImportMeshData *import_data,
Vector2D *poly_2d = new Vector2D[verts_per_poly];
Vector projected;
for (int i = 0; i < verts_per_poly; ++i) {
- projected = axis_matrix * vertices[verts_of_poly[i]];
+ projected = axis_matrix * vertex_storage[verts_of_poly[i]].v;
poly_2d[i][0] = projected[0];
poly_2d[i][1] = projected[1];
}
@@ -663,7 +663,6 @@ bool pushTriangle(int v1, int v2, int v3,
assert(triangle.b < triangle.c);
if (triangles_storage->find(triangle) == triangles_storage->end()) {
- face_indices->push_back(3);
face_indices->push_back(v1);
face_indices->push_back(v2);
face_indices->push_back(v3);
@@ -680,7 +679,7 @@ bool pushTriangle(int v1, int v2, int v3,
int carve_triangulatePoly(struct ImportMeshData *import_data,
CarveMeshImporter *mesh_importer,
- const std::vector<Vector> &vertices,
+ const std::vector<MeshSet<3>::vertex_t> &vertex_storage,
const int verts_per_poly,
const int *verts_of_poly,
const Matrix3 &axis_matrix,
@@ -725,14 +724,14 @@ int carve_triangulatePoly(struct ImportMeshData *import_data,
if (mesh_importer->triangulate2DPoly) {
triangulateNGon_importerTriangulator(import_data,
mesh_importer,
- vertices,
+ vertex_storage,
verts_per_poly,
verts_of_poly,
axis_matrix,
&triangles);
}
else {
- triangulateNGon_carveTriangulator(vertices,
+ triangulateNGon_carveTriangulator(vertex_storage,
verts_per_poly,
verts_of_poly,
axis_matrix,
diff --git a/extern/carve/carve-util.h b/extern/carve/carve-util.h
index f650810e9e3..0b509aa3cab 100644
--- a/extern/carve/carve-util.h
+++ b/extern/carve/carve-util.h
@@ -74,14 +74,14 @@ bool carve_unionIntersections(carve::csg::CSG *csg,
carve::mesh::MeshSet<3> **left_r,
carve::mesh::MeshSet<3> **right_r);
-bool carve_checkPolyPlanarAndGetNormal(const std::vector<carve::geom3d::Vector> &vertices,
+bool carve_checkPolyPlanarAndGetNormal(const std::vector<carve::mesh::MeshSet<3>::vertex_t> &vertex_storage,
const int verts_per_poly,
const int *verts_of_poly,
carve::math::Matrix3 *axis_matrix_r);
int carve_triangulatePoly(struct ImportMeshData *import_data,
CarveMeshImporter *mesh_importer,
- const std::vector<carve::geom3d::Vector> &vertices,
+ const std::vector<carve::mesh::MeshSet<3>::vertex_t> &vertex_storage,
const int verts_per_poly,
const int *verts_of_poly,
const carve::math::Matrix3 &axis_matrix,
diff --git a/extern/carve/include/carve/mesh_simplify.hpp b/extern/carve/include/carve/mesh_simplify.hpp
index f0a0a965707..f9544a4e969 100644
--- a/extern/carve/include/carve/mesh_simplify.hpp
+++ b/extern/carve/include/carve/mesh_simplify.hpp
@@ -1439,7 +1439,7 @@ namespace carve {
size_t removeLowVolumeManifolds(meshset_t *meshset, double min_abs_volume) {
- size_t n_removed;
+ size_t n_removed = 0;
for (size_t i = 0; i < meshset->meshes.size(); ++i) {
if (fabs(meshset->meshes[i]->volume()) < min_abs_volume) {
delete meshset->meshes[i];
diff --git a/extern/carve/patches/mesh_simplify_uninitialized_var.patch b/extern/carve/patches/mesh_simplify_uninitialized_var.patch
new file mode 100644
index 00000000000..592c6db0b68
--- /dev/null
+++ b/extern/carve/patches/mesh_simplify_uninitialized_var.patch
@@ -0,0 +1,12 @@
+diff -r e82d852e4fb0 include/carve/mesh_simplify.hpp
+--- a/include/carve/mesh_simplify.hpp Wed Jan 15 13:16:14 2014 +1100
++++ b/include/carve/mesh_simplify.hpp Wed Jun 11 13:13:09 2014 +0600
+@@ -1414,7 +1414,7 @@
+
+
+ size_t removeLowVolumeManifolds(meshset_t *meshset, double min_abs_volume) {
+- size_t n_removed;
++ size_t n_removed = 0;
+ for (size_t i = 0; i < meshset->meshes.size(); ++i) {
+ if (fabs(meshset->meshes[i]->volume()) < min_abs_volume) {
+ delete meshset->meshes[i];
diff --git a/extern/carve/patches/series b/extern/carve/patches/series
index 529bf43a858..4691339b419 100644
--- a/extern/carve/patches/series
+++ b/extern/carve/patches/series
@@ -7,6 +7,7 @@ clang_is_heap_fix.patch
strict_flags.patch
interpolator_reorder.patch
mesh_simplify_dissolve_edges.patch
+mesh_simplify_uninitialized_var.patch
memory_leak_fix.patch
msvc_fix.patch
face_hole_merge_workaround.patch
diff --git a/extern/gtest/CMakeLists.txt b/extern/gtest/CMakeLists.txt
new file mode 100644
index 00000000000..b5e40027513
--- /dev/null
+++ b/extern/gtest/CMakeLists.txt
@@ -0,0 +1,64 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2014, Blender Foundation
+# All rights reserved.
+#
+# Contributor(s): Sergey Sharybin
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ include
+)
+
+set(INC_SYS
+
+)
+
+set(SRC
+ src/gtest.cc
+ src/gtest-death-test.cc
+ src/gtest-filepath.cc
+ src/gtest-port.cc
+ src/gtest-printers.cc
+ src/gtest-test-part.cc
+ src/gtest-typed-test.cc
+
+ include/gtest/gtest-death-test.h
+ include/gtest/gtest.h
+ include/gtest/gtest-message.h
+ include/gtest/gtest-param-test.h
+ include/gtest/gtest_pred_impl.h
+ include/gtest/gtest-printers.h
+ include/gtest/gtest_prod.h
+ include/gtest/gtest-spi.h
+ include/gtest/gtest-test-part.h
+ include/gtest/gtest-typed-test.h
+ include/gtest/internal/gtest-death-test-internal.h
+ include/gtest/internal/gtest-filepath.h
+ include/gtest/internal/gtest-internal.h
+ include/gtest/internal/gtest-linked_ptr.h
+ include/gtest/internal/gtest-param-util-generated.h
+ include/gtest/internal/gtest-param-util.h
+ include/gtest/internal/gtest-port.h
+ include/gtest/internal/gtest-string.h
+ include/gtest/internal/gtest-tuple.h
+ include/gtest/internal/gtest-type-util.h
+)
+
+blender_add_lib(extern_gtest "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/extern/gtest/LICENSE b/extern/gtest/LICENSE
new file mode 100644
index 00000000000..1941a11f8ce
--- /dev/null
+++ b/extern/gtest/LICENSE
@@ -0,0 +1,28 @@
+Copyright 2008, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/extern/gtest/README b/extern/gtest/README
new file mode 100644
index 00000000000..27e4eff92c9
--- /dev/null
+++ b/extern/gtest/README
@@ -0,0 +1,7 @@
+Project: Google C++ Testing Framework
+URL: http://code.google.com/p/googletest
+License: New BSD
+Upstream version: 1.7.0
+Local modifications:
+
+None.
diff --git a/extern/gtest/include/gtest/gtest-death-test.h b/extern/gtest/include/gtest/gtest-death-test.h
new file mode 100644
index 00000000000..957a69c6a9e
--- /dev/null
+++ b/extern/gtest/include/gtest/gtest-death-test.h
@@ -0,0 +1,294 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file defines the public API for death tests. It is
+// #included by gtest.h so a user doesn't need to include this
+// directly.
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
+#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
+
+#include "gtest/internal/gtest-death-test-internal.h"
+
+namespace testing {
+
+// This flag controls the style of death tests. Valid values are "threadsafe",
+// meaning that the death test child process will re-execute the test binary
+// from the start, running only a single death test, or "fast",
+// meaning that the child process will execute the test logic immediately
+// after forking.
+GTEST_DECLARE_string_(death_test_style);
+
+#if GTEST_HAS_DEATH_TEST
+
+namespace internal {
+
+// Returns a Boolean value indicating whether the caller is currently
+// executing in the context of the death test child process. Tools such as
+// Valgrind heap checkers may need this to modify their behavior in death
+// tests. IMPORTANT: This is an internal utility. Using it may break the
+// implementation of death tests. User code MUST NOT use it.
+GTEST_API_ bool InDeathTestChild();
+
+} // namespace internal
+
+// The following macros are useful for writing death tests.
+
+// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is
+// executed:
+//
+// 1. It generates a warning if there is more than one active
+// thread. This is because it's safe to fork() or clone() only
+// when there is a single thread.
+//
+// 2. The parent process clone()s a sub-process and runs the death
+// test in it; the sub-process exits with code 0 at the end of the
+// death test, if it hasn't exited already.
+//
+// 3. The parent process waits for the sub-process to terminate.
+//
+// 4. The parent process checks the exit code and error message of
+// the sub-process.
+//
+// Examples:
+//
+// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number");
+// for (int i = 0; i < 5; i++) {
+// EXPECT_DEATH(server.ProcessRequest(i),
+// "Invalid request .* in ProcessRequest()")
+// << "Failed to die on request " << i;
+// }
+//
+// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting");
+//
+// bool KilledBySIGHUP(int exit_code) {
+// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP;
+// }
+//
+// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!");
+//
+// On the regular expressions used in death tests:
+//
+// On POSIX-compliant systems (*nix), we use the <regex.h> library,
+// which uses the POSIX extended regex syntax.
+//
+// On other platforms (e.g. Windows), we only support a simple regex
+// syntax implemented as part of Google Test. This limited
+// implementation should be enough most of the time when writing
+// death tests; though it lacks many features you can find in PCRE
+// or POSIX extended regex syntax. For example, we don't support
+// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and
+// repetition count ("x{5,7}"), among others.
+//
+// Below is the syntax that we do support. We chose it to be a
+// subset of both PCRE and POSIX extended regex, so it's easy to
+// learn wherever you come from. In the following: 'A' denotes a
+// literal character, period (.), or a single \\ escape sequence;
+// 'x' and 'y' denote regular expressions; 'm' and 'n' are for
+// natural numbers.
+//
+// c matches any literal character c
+// \\d matches any decimal digit
+// \\D matches any character that's not a decimal digit
+// \\f matches \f
+// \\n matches \n
+// \\r matches \r
+// \\s matches any ASCII whitespace, including \n
+// \\S matches any character that's not a whitespace
+// \\t matches \t
+// \\v matches \v
+// \\w matches any letter, _, or decimal digit
+// \\W matches any character that \\w doesn't match
+// \\c matches any literal character c, which must be a punctuation
+// . matches any single character except \n
+// A? matches 0 or 1 occurrences of A
+// A* matches 0 or many occurrences of A
+// A+ matches 1 or many occurrences of A
+// ^ matches the beginning of a string (not that of each line)
+// $ matches the end of a string (not that of each line)
+// xy matches x followed by y
+//
+// If you accidentally use PCRE or POSIX extended regex features
+// not implemented by us, you will get a run-time failure. In that
+// case, please try to rewrite your regular expression within the
+// above syntax.
+//
+// This implementation is *not* meant to be as highly tuned or robust
+// as a compiled regex library, but should perform well enough for a
+// death test, which already incurs significant overhead by launching
+// a child process.
+//
+// Known caveats:
+//
+// A "threadsafe" style death test obtains the path to the test
+// program from argv[0] and re-executes it in the sub-process. For
+// simplicity, the current implementation doesn't search the PATH
+// when launching the sub-process. This means that the user must
+// invoke the test program via a path that contains at least one
+// path separator (e.g. path/to/foo_test and
+// /absolute/path/to/bar_test are fine, but foo_test is not). This
+// is rarely a problem as people usually don't put the test binary
+// directory in PATH.
+//
+// TODO(wan@google.com): make thread-safe death tests search the PATH.
+
+// Asserts that a given statement causes the program to exit, with an
+// integer exit status that satisfies predicate, and emitting error output
+// that matches regex.
+# define ASSERT_EXIT(statement, predicate, regex) \
+ GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_)
+
+// Like ASSERT_EXIT, but continues on to successive tests in the
+// test case, if any:
+# define EXPECT_EXIT(statement, predicate, regex) \
+ GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_)
+
+// Asserts that a given statement causes the program to exit, either by
+// explicitly exiting with a nonzero exit code or being killed by a
+// signal, and emitting error output that matches regex.
+# define ASSERT_DEATH(statement, regex) \
+ ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex)
+
+// Like ASSERT_DEATH, but continues on to successive tests in the
+// test case, if any:
+# define EXPECT_DEATH(statement, regex) \
+ EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex)
+
+// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*:
+
+// Tests that an exit code describes a normal exit with a given exit code.
+class GTEST_API_ ExitedWithCode {
+ public:
+ explicit ExitedWithCode(int exit_code);
+ bool operator()(int exit_status) const;
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ExitedWithCode& other);
+
+ const int exit_code_;
+};
+
+# if !GTEST_OS_WINDOWS
+// Tests that an exit code describes an exit due to termination by a
+// given signal.
+class GTEST_API_ KilledBySignal {
+ public:
+ explicit KilledBySignal(int signum);
+ bool operator()(int exit_status) const;
+ private:
+ const int signum_;
+};
+# endif // !GTEST_OS_WINDOWS
+
+// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode.
+// The death testing framework causes this to have interesting semantics,
+// since the sideeffects of the call are only visible in opt mode, and not
+// in debug mode.
+//
+// In practice, this can be used to test functions that utilize the
+// LOG(DFATAL) macro using the following style:
+//
+// int DieInDebugOr12(int* sideeffect) {
+// if (sideeffect) {
+// *sideeffect = 12;
+// }
+// LOG(DFATAL) << "death";
+// return 12;
+// }
+//
+// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) {
+// int sideeffect = 0;
+// // Only asserts in dbg.
+// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death");
+//
+// #ifdef NDEBUG
+// // opt-mode has sideeffect visible.
+// EXPECT_EQ(12, sideeffect);
+// #else
+// // dbg-mode no visible sideeffect.
+// EXPECT_EQ(0, sideeffect);
+// #endif
+// }
+//
+// This will assert that DieInDebugReturn12InOpt() crashes in debug
+// mode, usually due to a DCHECK or LOG(DFATAL), but returns the
+// appropriate fallback value (12 in this case) in opt mode. If you
+// need to test that a function has appropriate side-effects in opt
+// mode, include assertions against the side-effects. A general
+// pattern for this is:
+//
+// EXPECT_DEBUG_DEATH({
+// // Side-effects here will have an effect after this statement in
+// // opt mode, but none in debug mode.
+// EXPECT_EQ(12, DieInDebugOr12(&sideeffect));
+// }, "death");
+//
+# ifdef NDEBUG
+
+# define EXPECT_DEBUG_DEATH(statement, regex) \
+ GTEST_EXECUTE_STATEMENT_(statement, regex)
+
+# define ASSERT_DEBUG_DEATH(statement, regex) \
+ GTEST_EXECUTE_STATEMENT_(statement, regex)
+
+# else
+
+# define EXPECT_DEBUG_DEATH(statement, regex) \
+ EXPECT_DEATH(statement, regex)
+
+# define ASSERT_DEBUG_DEATH(statement, regex) \
+ ASSERT_DEATH(statement, regex)
+
+# endif // NDEBUG for EXPECT_DEBUG_DEATH
+#endif // GTEST_HAS_DEATH_TEST
+
+// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and
+// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if
+// death tests are supported; otherwise they just issue a warning. This is
+// useful when you are combining death test assertions with normal test
+// assertions in one test.
+#if GTEST_HAS_DEATH_TEST
+# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
+ EXPECT_DEATH(statement, regex)
+# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
+ ASSERT_DEATH(statement, regex)
+#else
+# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
+ GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, )
+# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
+ GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return)
+#endif
+
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
diff --git a/extern/gtest/include/gtest/gtest-message.h b/extern/gtest/include/gtest/gtest-message.h
new file mode 100644
index 00000000000..fe879bca792
--- /dev/null
+++ b/extern/gtest/include/gtest/gtest-message.h
@@ -0,0 +1,250 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file defines the Message class.
+//
+// IMPORTANT NOTE: Due to limitation of the C++ language, we have to
+// leave some internal implementation details in this header file.
+// They are clearly marked by comments like this:
+//
+// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+//
+// Such code is NOT meant to be used by a user directly, and is subject
+// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user
+// program!
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
+#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
+
+#include <limits>
+
+#include "gtest/internal/gtest-port.h"
+
+// Ensures that there is at least one operator<< in the global namespace.
+// See Message& operator<<(...) below for why.
+void operator<<(const testing::internal::Secret&, int);
+
+namespace testing {
+
+// The Message class works like an ostream repeater.
+//
+// Typical usage:
+//
+// 1. You stream a bunch of values to a Message object.
+// It will remember the text in a stringstream.
+// 2. Then you stream the Message object to an ostream.
+// This causes the text in the Message to be streamed
+// to the ostream.
+//
+// For example;
+//
+// testing::Message foo;
+// foo << 1 << " != " << 2;
+// std::cout << foo;
+//
+// will print "1 != 2".
+//
+// Message is not intended to be inherited from. In particular, its
+// destructor is not virtual.
+//
+// Note that stringstream behaves differently in gcc and in MSVC. You
+// can stream a NULL char pointer to it in the former, but not in the
+// latter (it causes an access violation if you do). The Message
+// class hides this difference by treating a NULL char pointer as
+// "(null)".
+class GTEST_API_ Message {
+ private:
+ // The type of basic IO manipulators (endl, ends, and flush) for
+ // narrow streams.
+ typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&);
+
+ public:
+ // Constructs an empty Message.
+ Message();
+
+ // Copy constructor.
+ Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT
+ *ss_ << msg.GetString();
+ }
+
+ // Constructs a Message from a C-string.
+ explicit Message(const char* str) : ss_(new ::std::stringstream) {
+ *ss_ << str;
+ }
+
+#if GTEST_OS_SYMBIAN
+ // Streams a value (either a pointer or not) to this object.
+ template <typename T>
+ inline Message& operator <<(const T& value) {
+ StreamHelper(typename internal::is_pointer<T>::type(), value);
+ return *this;
+ }
+#else
+ // Streams a non-pointer value to this object.
+ template <typename T>
+ inline Message& operator <<(const T& val) {
+ // Some libraries overload << for STL containers. These
+ // overloads are defined in the global namespace instead of ::std.
+ //
+ // C++'s symbol lookup rule (i.e. Koenig lookup) says that these
+ // overloads are visible in either the std namespace or the global
+ // namespace, but not other namespaces, including the testing
+ // namespace which Google Test's Message class is in.
+ //
+ // To allow STL containers (and other types that has a << operator
+ // defined in the global namespace) to be used in Google Test
+ // assertions, testing::Message must access the custom << operator
+ // from the global namespace. With this using declaration,
+ // overloads of << defined in the global namespace and those
+ // visible via Koenig lookup are both exposed in this function.
+ using ::operator <<;
+ *ss_ << val;
+ return *this;
+ }
+
+ // Streams a pointer value to this object.
+ //
+ // This function is an overload of the previous one. When you
+ // stream a pointer to a Message, this definition will be used as it
+ // is more specialized. (The C++ Standard, section
+ // [temp.func.order].) If you stream a non-pointer, then the
+ // previous definition will be used.
+ //
+ // The reason for this overload is that streaming a NULL pointer to
+ // ostream is undefined behavior. Depending on the compiler, you
+ // may get "0", "(nil)", "(null)", or an access violation. To
+ // ensure consistent result across compilers, we always treat NULL
+ // as "(null)".
+ template <typename T>
+ inline Message& operator <<(T* const& pointer) { // NOLINT
+ if (pointer == NULL) {
+ *ss_ << "(null)";
+ } else {
+ *ss_ << pointer;
+ }
+ return *this;
+ }
+#endif // GTEST_OS_SYMBIAN
+
+ // Since the basic IO manipulators are overloaded for both narrow
+ // and wide streams, we have to provide this specialized definition
+ // of operator <<, even though its body is the same as the
+ // templatized version above. Without this definition, streaming
+ // endl or other basic IO manipulators to Message will confuse the
+ // compiler.
+ Message& operator <<(BasicNarrowIoManip val) {
+ *ss_ << val;
+ return *this;
+ }
+
+ // Instead of 1/0, we want to see true/false for bool values.
+ Message& operator <<(bool b) {
+ return *this << (b ? "true" : "false");
+ }
+
+ // These two overloads allow streaming a wide C string to a Message
+ // using the UTF-8 encoding.
+ Message& operator <<(const wchar_t* wide_c_str);
+ Message& operator <<(wchar_t* wide_c_str);
+
+#if GTEST_HAS_STD_WSTRING
+ // Converts the given wide string to a narrow string using the UTF-8
+ // encoding, and streams the result to this Message object.
+ Message& operator <<(const ::std::wstring& wstr);
+#endif // GTEST_HAS_STD_WSTRING
+
+#if GTEST_HAS_GLOBAL_WSTRING
+ // Converts the given wide string to a narrow string using the UTF-8
+ // encoding, and streams the result to this Message object.
+ Message& operator <<(const ::wstring& wstr);
+#endif // GTEST_HAS_GLOBAL_WSTRING
+
+ // Gets the text streamed to this object so far as an std::string.
+ // Each '\0' character in the buffer is replaced with "\\0".
+ //
+ // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+ std::string GetString() const;
+
+ private:
+
+#if GTEST_OS_SYMBIAN
+ // These are needed as the Nokia Symbian Compiler cannot decide between
+ // const T& and const T* in a function template. The Nokia compiler _can_
+ // decide between class template specializations for T and T*, so a
+ // tr1::type_traits-like is_pointer works, and we can overload on that.
+ template <typename T>
+ inline void StreamHelper(internal::true_type /*is_pointer*/, T* pointer) {
+ if (pointer == NULL) {
+ *ss_ << "(null)";
+ } else {
+ *ss_ << pointer;
+ }
+ }
+ template <typename T>
+ inline void StreamHelper(internal::false_type /*is_pointer*/,
+ const T& value) {
+ // See the comments in Message& operator <<(const T&) above for why
+ // we need this using statement.
+ using ::operator <<;
+ *ss_ << value;
+ }
+#endif // GTEST_OS_SYMBIAN
+
+ // We'll hold the text streamed to this object here.
+ const internal::scoped_ptr< ::std::stringstream> ss_;
+
+ // We declare (but don't implement) this to prevent the compiler
+ // from implementing the assignment operator.
+ void operator=(const Message&);
+};
+
+// Streams a Message to an ostream.
+inline std::ostream& operator <<(std::ostream& os, const Message& sb) {
+ return os << sb.GetString();
+}
+
+namespace internal {
+
+// Converts a streamable value to an std::string. A NULL pointer is
+// converted to "(null)". When the input value is a ::string,
+// ::std::string, ::wstring, or ::std::wstring object, each NUL
+// character in it is replaced with "\\0".
+template <typename T>
+std::string StreamableToString(const T& streamable) {
+ return (Message() << streamable).GetString();
+}
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
diff --git a/extern/gtest/include/gtest/gtest-param-test.h b/extern/gtest/include/gtest/gtest-param-test.h
new file mode 100644
index 00000000000..d6702c8f162
--- /dev/null
+++ b/extern/gtest/include/gtest/gtest-param-test.h
@@ -0,0 +1,1421 @@
+// This file was GENERATED by command:
+// pump.py gtest-param-test.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: vladl@google.com (Vlad Losev)
+//
+// Macros and functions for implementing parameterized tests
+// in Google C++ Testing Framework (Google Test)
+//
+// This file is generated by a SCRIPT. DO NOT EDIT BY HAND!
+//
+#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
+#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
+
+
+// Value-parameterized tests allow you to test your code with different
+// parameters without writing multiple copies of the same test.
+//
+// Here is how you use value-parameterized tests:
+
+#if 0
+
+// To write value-parameterized tests, first you should define a fixture
+// class. It is usually derived from testing::TestWithParam<T> (see below for
+// another inheritance scheme that's sometimes useful in more complicated
+// class hierarchies), where the type of your parameter values.
+// TestWithParam<T> is itself derived from testing::Test. T can be any
+// copyable type. If it's a raw pointer, you are responsible for managing the
+// lifespan of the pointed values.
+
+class FooTest : public ::testing::TestWithParam<const char*> {
+ // You can implement all the usual class fixture members here.
+};
+
+// Then, use the TEST_P macro to define as many parameterized tests
+// for this fixture as you want. The _P suffix is for "parameterized"
+// or "pattern", whichever you prefer to think.
+
+TEST_P(FooTest, DoesBlah) {
+ // Inside a test, access the test parameter with the GetParam() method
+ // of the TestWithParam<T> class:
+ EXPECT_TRUE(foo.Blah(GetParam()));
+ ...
+}
+
+TEST_P(FooTest, HasBlahBlah) {
+ ...
+}
+
+// Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test
+// case with any set of parameters you want. Google Test defines a number
+// of functions for generating test parameters. They return what we call
+// (surprise!) parameter generators. Here is a summary of them, which
+// are all in the testing namespace:
+//
+//
+// Range(begin, end [, step]) - Yields values {begin, begin+step,
+// begin+step+step, ...}. The values do not
+// include end. step defaults to 1.
+// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}.
+// ValuesIn(container) - Yields values from a C-style array, an STL
+// ValuesIn(begin,end) container, or an iterator range [begin, end).
+// Bool() - Yields sequence {false, true}.
+// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product
+// for the math savvy) of the values generated
+// by the N generators.
+//
+// For more details, see comments at the definitions of these functions below
+// in this file.
+//
+// The following statement will instantiate tests from the FooTest test case
+// each with parameter values "meeny", "miny", and "moe".
+
+INSTANTIATE_TEST_CASE_P(InstantiationName,
+ FooTest,
+ Values("meeny", "miny", "moe"));
+
+// To distinguish different instances of the pattern, (yes, you
+// can instantiate it more then once) the first argument to the
+// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the
+// actual test case name. Remember to pick unique prefixes for different
+// instantiations. The tests from the instantiation above will have
+// these names:
+//
+// * InstantiationName/FooTest.DoesBlah/0 for "meeny"
+// * InstantiationName/FooTest.DoesBlah/1 for "miny"
+// * InstantiationName/FooTest.DoesBlah/2 for "moe"
+// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny"
+// * InstantiationName/FooTest.HasBlahBlah/1 for "miny"
+// * InstantiationName/FooTest.HasBlahBlah/2 for "moe"
+//
+// You can use these names in --gtest_filter.
+//
+// This statement will instantiate all tests from FooTest again, each
+// with parameter values "cat" and "dog":
+
+const char* pets[] = {"cat", "dog"};
+INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets));
+
+// The tests from the instantiation above will have these names:
+//
+// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat"
+// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog"
+// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat"
+// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog"
+//
+// Please note that INSTANTIATE_TEST_CASE_P will instantiate all tests
+// in the given test case, whether their definitions come before or
+// AFTER the INSTANTIATE_TEST_CASE_P statement.
+//
+// Please also note that generator expressions (including parameters to the
+// generators) are evaluated in InitGoogleTest(), after main() has started.
+// This allows the user on one hand, to adjust generator parameters in order
+// to dynamically determine a set of tests to run and on the other hand,
+// give the user a chance to inspect the generated tests with Google Test
+// reflection API before RUN_ALL_TESTS() is executed.
+//
+// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc
+// for more examples.
+//
+// In the future, we plan to publish the API for defining new parameter
+// generators. But for now this interface remains part of the internal
+// implementation and is subject to change.
+//
+//
+// A parameterized test fixture must be derived from testing::Test and from
+// testing::WithParamInterface<T>, where T is the type of the parameter
+// values. Inheriting from TestWithParam<T> satisfies that requirement because
+// TestWithParam<T> inherits from both Test and WithParamInterface. In more
+// complicated hierarchies, however, it is occasionally useful to inherit
+// separately from Test and WithParamInterface. For example:
+
+class BaseTest : public ::testing::Test {
+ // You can inherit all the usual members for a non-parameterized test
+ // fixture here.
+};
+
+class DerivedTest : public BaseTest, public ::testing::WithParamInterface<int> {
+ // The usual test fixture members go here too.
+};
+
+TEST_F(BaseTest, HasFoo) {
+ // This is an ordinary non-parameterized test.
+}
+
+TEST_P(DerivedTest, DoesBlah) {
+ // GetParam works just the same here as if you inherit from TestWithParam.
+ EXPECT_TRUE(foo.Blah(GetParam()));
+}
+
+#endif // 0
+
+#include "gtest/internal/gtest-port.h"
+
+#if !GTEST_OS_SYMBIAN
+# include <utility>
+#endif
+
+// scripts/fuse_gtest.py depends on gtest's own header being #included
+// *unconditionally*. Therefore these #includes cannot be moved
+// inside #if GTEST_HAS_PARAM_TEST.
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-param-util.h"
+#include "gtest/internal/gtest-param-util-generated.h"
+
+#if GTEST_HAS_PARAM_TEST
+
+namespace testing {
+
+// Functions producing parameter generators.
+//
+// Google Test uses these generators to produce parameters for value-
+// parameterized tests. When a parameterized test case is instantiated
+// with a particular generator, Google Test creates and runs tests
+// for each element in the sequence produced by the generator.
+//
+// In the following sample, tests from test case FooTest are instantiated
+// each three times with parameter values 3, 5, and 8:
+//
+// class FooTest : public TestWithParam<int> { ... };
+//
+// TEST_P(FooTest, TestThis) {
+// }
+// TEST_P(FooTest, TestThat) {
+// }
+// INSTANTIATE_TEST_CASE_P(TestSequence, FooTest, Values(3, 5, 8));
+//
+
+// Range() returns generators providing sequences of values in a range.
+//
+// Synopsis:
+// Range(start, end)
+// - returns a generator producing a sequence of values {start, start+1,
+// start+2, ..., }.
+// Range(start, end, step)
+// - returns a generator producing a sequence of values {start, start+step,
+// start+step+step, ..., }.
+// Notes:
+// * The generated sequences never include end. For example, Range(1, 5)
+// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2)
+// returns a generator producing {1, 3, 5, 7}.
+// * start and end must have the same type. That type may be any integral or
+// floating-point type or a user defined type satisfying these conditions:
+// * It must be assignable (have operator=() defined).
+// * It must have operator+() (operator+(int-compatible type) for
+// two-operand version).
+// * It must have operator<() defined.
+// Elements in the resulting sequences will also have that type.
+// * Condition start < end must be satisfied in order for resulting sequences
+// to contain any elements.
+//
+template <typename T, typename IncrementT>
+internal::ParamGenerator<T> Range(T start, T end, IncrementT step) {
+ return internal::ParamGenerator<T>(
+ new internal::RangeGenerator<T, IncrementT>(start, end, step));
+}
+
+template <typename T>
+internal::ParamGenerator<T> Range(T start, T end) {
+ return Range(start, end, 1);
+}
+
+// ValuesIn() function allows generation of tests with parameters coming from
+// a container.
+//
+// Synopsis:
+// ValuesIn(const T (&array)[N])
+// - returns a generator producing sequences with elements from
+// a C-style array.
+// ValuesIn(const Container& container)
+// - returns a generator producing sequences with elements from
+// an STL-style container.
+// ValuesIn(Iterator begin, Iterator end)
+// - returns a generator producing sequences with elements from
+// a range [begin, end) defined by a pair of STL-style iterators. These
+// iterators can also be plain C pointers.
+//
+// Please note that ValuesIn copies the values from the containers
+// passed in and keeps them to generate tests in RUN_ALL_TESTS().
+//
+// Examples:
+//
+// This instantiates tests from test case StringTest
+// each with C-string values of "foo", "bar", and "baz":
+//
+// const char* strings[] = {"foo", "bar", "baz"};
+// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings));
+//
+// This instantiates tests from test case StlStringTest
+// each with STL strings with values "a" and "b":
+//
+// ::std::vector< ::std::string> GetParameterStrings() {
+// ::std::vector< ::std::string> v;
+// v.push_back("a");
+// v.push_back("b");
+// return v;
+// }
+//
+// INSTANTIATE_TEST_CASE_P(CharSequence,
+// StlStringTest,
+// ValuesIn(GetParameterStrings()));
+//
+//
+// This will also instantiate tests from CharTest
+// each with parameter values 'a' and 'b':
+//
+// ::std::list<char> GetParameterChars() {
+// ::std::list<char> list;
+// list.push_back('a');
+// list.push_back('b');
+// return list;
+// }
+// ::std::list<char> l = GetParameterChars();
+// INSTANTIATE_TEST_CASE_P(CharSequence2,
+// CharTest,
+// ValuesIn(l.begin(), l.end()));
+//
+template <typename ForwardIterator>
+internal::ParamGenerator<
+ typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type>
+ValuesIn(ForwardIterator begin, ForwardIterator end) {
+ typedef typename ::testing::internal::IteratorTraits<ForwardIterator>
+ ::value_type ParamType;
+ return internal::ParamGenerator<ParamType>(
+ new internal::ValuesInIteratorRangeGenerator<ParamType>(begin, end));
+}
+
+template <typename T, size_t N>
+internal::ParamGenerator<T> ValuesIn(const T (&array)[N]) {
+ return ValuesIn(array, array + N);
+}
+
+template <class Container>
+internal::ParamGenerator<typename Container::value_type> ValuesIn(
+ const Container& container) {
+ return ValuesIn(container.begin(), container.end());
+}
+
+// Values() allows generating tests from explicitly specified list of
+// parameters.
+//
+// Synopsis:
+// Values(T v1, T v2, ..., T vN)
+// - returns a generator producing sequences with elements v1, v2, ..., vN.
+//
+// For example, this instantiates tests from test case BarTest each
+// with values "one", "two", and "three":
+//
+// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three"));
+//
+// This instantiates tests from test case BazTest each with values 1, 2, 3.5.
+// The exact type of values will depend on the type of parameter in BazTest.
+//
+// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5));
+//
+// Currently, Values() supports from 1 to 50 parameters.
+//
+template <typename T1>
+internal::ValueArray1<T1> Values(T1 v1) {
+ return internal::ValueArray1<T1>(v1);
+}
+
+template <typename T1, typename T2>
+internal::ValueArray2<T1, T2> Values(T1 v1, T2 v2) {
+ return internal::ValueArray2<T1, T2>(v1, v2);
+}
+
+template <typename T1, typename T2, typename T3>
+internal::ValueArray3<T1, T2, T3> Values(T1 v1, T2 v2, T3 v3) {
+ return internal::ValueArray3<T1, T2, T3>(v1, v2, v3);
+}
+
+template <typename T1, typename T2, typename T3, typename T4>
+internal::ValueArray4<T1, T2, T3, T4> Values(T1 v1, T2 v2, T3 v3, T4 v4) {
+ return internal::ValueArray4<T1, T2, T3, T4>(v1, v2, v3, v4);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+internal::ValueArray5<T1, T2, T3, T4, T5> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+ T5 v5) {
+ return internal::ValueArray5<T1, T2, T3, T4, T5>(v1, v2, v3, v4, v5);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+internal::ValueArray6<T1, T2, T3, T4, T5, T6> Values(T1 v1, T2 v2, T3 v3,
+ T4 v4, T5 v5, T6 v6) {
+ return internal::ValueArray6<T1, T2, T3, T4, T5, T6>(v1, v2, v3, v4, v5, v6);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+internal::ValueArray7<T1, T2, T3, T4, T5, T6, T7> Values(T1 v1, T2 v2, T3 v3,
+ T4 v4, T5 v5, T6 v6, T7 v7) {
+ return internal::ValueArray7<T1, T2, T3, T4, T5, T6, T7>(v1, v2, v3, v4, v5,
+ v6, v7);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+internal::ValueArray8<T1, T2, T3, T4, T5, T6, T7, T8> Values(T1 v1, T2 v2,
+ T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8) {
+ return internal::ValueArray8<T1, T2, T3, T4, T5, T6, T7, T8>(v1, v2, v3, v4,
+ v5, v6, v7, v8);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+internal::ValueArray9<T1, T2, T3, T4, T5, T6, T7, T8, T9> Values(T1 v1, T2 v2,
+ T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9) {
+ return internal::ValueArray9<T1, T2, T3, T4, T5, T6, T7, T8, T9>(v1, v2, v3,
+ v4, v5, v6, v7, v8, v9);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+internal::ValueArray10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> Values(T1 v1,
+ T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10) {
+ return internal::ValueArray10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(v1,
+ v2, v3, v4, v5, v6, v7, v8, v9, v10);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11>
+internal::ValueArray11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10,
+ T11> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11) {
+ return internal::ValueArray11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10,
+ T11>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12>
+internal::ValueArray12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12) {
+ return internal::ValueArray12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13>
+internal::ValueArray13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13) {
+ return internal::ValueArray13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14>
+internal::ValueArray14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) {
+ return internal::ValueArray14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
+ v14);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15>
+internal::ValueArray15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8,
+ T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) {
+ return internal::ValueArray15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
+ v13, v14, v15);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16>
+internal::ValueArray16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+ T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16) {
+ return internal::ValueArray16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
+ v12, v13, v14, v15, v16);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17>
+internal::ValueArray17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+ T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16, T17 v17) {
+ return internal::ValueArray17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,
+ v11, v12, v13, v14, v15, v16, v17);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18>
+internal::ValueArray18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6,
+ T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16, T17 v17, T18 v18) {
+ return internal::ValueArray18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18>(v1, v2, v3, v4, v5, v6, v7, v8, v9,
+ v10, v11, v12, v13, v14, v15, v16, v17, v18);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19>
+internal::ValueArray19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5,
+ T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14,
+ T15 v15, T16 v16, T17 v17, T18 v18, T19 v19) {
+ return internal::ValueArray19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19>(v1, v2, v3, v4, v5, v6, v7, v8,
+ v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20>
+internal::ValueArray20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+ T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
+ T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20) {
+ return internal::ValueArray20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20>(v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21>
+internal::ValueArray21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+ T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
+ T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21) {
+ return internal::ValueArray21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21>(v1, v2, v3, v4, v5, v6,
+ v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22>
+internal::ValueArray22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22> Values(T1 v1, T2 v2, T3 v3,
+ T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+ T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+ T21 v21, T22 v22) {
+ return internal::ValueArray22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22>(v1, v2, v3, v4,
+ v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
+ v20, v21, v22);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23>
+internal::ValueArray23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23> Values(T1 v1, T2 v2,
+ T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+ T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+ T21 v21, T22 v22, T23 v23) {
+ return internal::ValueArray23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23>(v1, v2, v3,
+ v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
+ v20, v21, v22, v23);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24>
+internal::ValueArray24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> Values(T1 v1, T2 v2,
+ T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+ T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+ T21 v21, T22 v22, T23 v23, T24 v24) {
+ return internal::ValueArray24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24>(v1, v2,
+ v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18,
+ v19, v20, v21, v22, v23, v24);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25>
+internal::ValueArray25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> Values(T1 v1,
+ T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11,
+ T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19,
+ T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25) {
+ return internal::ValueArray25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25>(v1,
+ v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
+ v18, v19, v20, v21, v22, v23, v24, v25);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26>
+internal::ValueArray26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26) {
+ return internal::ValueArray26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15,
+ v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27>
+internal::ValueArray27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27) {
+ return internal::ValueArray27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14,
+ v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28>
+internal::ValueArray28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28) {
+ return internal::ValueArray28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
+ v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27,
+ v28);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29>
+internal::ValueArray29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29) {
+ return internal::ValueArray29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
+ v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26,
+ v27, v28, v29);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30>
+internal::ValueArray30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8,
+ T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16,
+ T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24,
+ T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) {
+ return internal::ValueArray30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
+ v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25,
+ v26, v27, v28, v29, v30);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31>
+internal::ValueArray31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+ T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+ T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) {
+ return internal::ValueArray31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,
+ v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24,
+ v25, v26, v27, v28, v29, v30, v31);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32>
+internal::ValueArray32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+ T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+ T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
+ T32 v32) {
+ return internal::ValueArray32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32>(v1, v2, v3, v4, v5, v6, v7, v8, v9,
+ v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23,
+ v24, v25, v26, v27, v28, v29, v30, v31, v32);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33>
+internal::ValueArray33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6,
+ T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+ T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
+ T32 v32, T33 v33) {
+ return internal::ValueArray33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33>(v1, v2, v3, v4, v5, v6, v7, v8,
+ v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23,
+ v24, v25, v26, v27, v28, v29, v30, v31, v32, v33);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34>
+internal::ValueArray34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5,
+ T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14,
+ T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22,
+ T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30,
+ T31 v31, T32 v32, T33 v33, T34 v34) {
+ return internal::ValueArray34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34>(v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
+ v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35>
+internal::ValueArray35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+ T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
+ T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21,
+ T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29,
+ T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35) {
+ return internal::ValueArray35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35>(v1, v2, v3, v4, v5, v6,
+ v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21,
+ v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36>
+internal::ValueArray36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+ T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
+ T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21,
+ T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29,
+ T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36) {
+ return internal::ValueArray36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36>(v1, v2, v3, v4,
+ v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
+ v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33,
+ v34, v35, v36);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37>
+internal::ValueArray37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37> Values(T1 v1, T2 v2, T3 v3,
+ T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+ T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+ T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28,
+ T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36,
+ T37 v37) {
+ return internal::ValueArray37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37>(v1, v2, v3,
+ v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
+ v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33,
+ v34, v35, v36, v37);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38>
+internal::ValueArray38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> Values(T1 v1, T2 v2,
+ T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+ T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+ T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28,
+ T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36,
+ T37 v37, T38 v38) {
+ return internal::ValueArray38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38>(v1, v2,
+ v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18,
+ v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32,
+ v33, v34, v35, v36, v37, v38);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39>
+internal::ValueArray39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> Values(T1 v1, T2 v2,
+ T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+ T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+ T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28,
+ T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36,
+ T37 v37, T38 v38, T39 v39) {
+ return internal::ValueArray39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39>(v1,
+ v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
+ v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31,
+ v32, v33, v34, v35, v36, v37, v38, v39);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40>
+internal::ValueArray40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> Values(T1 v1,
+ T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11,
+ T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19,
+ T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27,
+ T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35,
+ T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) {
+ return internal::ValueArray40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15,
+ v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29,
+ v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41>
+internal::ValueArray41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41) {
+ return internal::ValueArray41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14,
+ v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28,
+ v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42>
+internal::ValueArray42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42) {
+ return internal::ValueArray42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
+ v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27,
+ v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41,
+ v42);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43>
+internal::ValueArray43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43) {
+ return internal::ValueArray43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42, T43>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
+ v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26,
+ v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40,
+ v41, v42, v43);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44>
+internal::ValueArray44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43, T44 v44) {
+ return internal::ValueArray44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42, T43, T44>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
+ v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25,
+ v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39,
+ v40, v41, v42, v43, v44);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45>
+internal::ValueArray45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8,
+ T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16,
+ T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24,
+ T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32,
+ T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40,
+ T41 v41, T42 v42, T43 v43, T44 v44, T45 v45) {
+ return internal::ValueArray45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42, T43, T44, T45>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,
+ v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24,
+ v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38,
+ v39, v40, v41, v42, v43, v44, v45);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46>
+internal::ValueArray46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+ T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+ T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
+ T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39,
+ T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) {
+ return internal::ValueArray46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42, T43, T44, T45, T46>(v1, v2, v3, v4, v5, v6, v7, v8, v9,
+ v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23,
+ v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37,
+ v38, v39, v40, v41, v42, v43, v44, v45, v46);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47>
+internal::ValueArray47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+ T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+ T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
+ T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39,
+ T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) {
+ return internal::ValueArray47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42, T43, T44, T45, T46, T47>(v1, v2, v3, v4, v5, v6, v7, v8,
+ v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23,
+ v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37,
+ v38, v39, v40, v41, v42, v43, v44, v45, v46, v47);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48>
+internal::ValueArray48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47, T48> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6,
+ T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+ T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+ T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
+ T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39,
+ T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47,
+ T48 v48) {
+ return internal::ValueArray48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42, T43, T44, T45, T46, T47, T48>(v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
+ v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36,
+ v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48, typename T49>
+internal::ValueArray49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47, T48, T49> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5,
+ T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14,
+ T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22,
+ T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30,
+ T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38,
+ T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46,
+ T47 v47, T48 v48, T49 v49) {
+ return internal::ValueArray49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42, T43, T44, T45, T46, T47, T48, T49>(v1, v2, v3, v4, v5, v6,
+ v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21,
+ v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35,
+ v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48, typename T49, typename T50>
+internal::ValueArray50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47, T48, T49, T50> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+ T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
+ T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21,
+ T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29,
+ T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37,
+ T38 v38, T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45,
+ T46 v46, T47 v47, T48 v48, T49 v49, T50 v50) {
+ return internal::ValueArray50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40, T41, T42, T43, T44, T45, T46, T47, T48, T49, T50>(v1, v2, v3, v4,
+ v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
+ v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33,
+ v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47,
+ v48, v49, v50);
+}
+
+// Bool() allows generating tests with parameters in a set of (false, true).
+//
+// Synopsis:
+// Bool()
+// - returns a generator producing sequences with elements {false, true}.
+//
+// It is useful when testing code that depends on Boolean flags. Combinations
+// of multiple flags can be tested when several Bool()'s are combined using
+// Combine() function.
+//
+// In the following example all tests in the test case FlagDependentTest
+// will be instantiated twice with parameters false and true.
+//
+// class FlagDependentTest : public testing::TestWithParam<bool> {
+// virtual void SetUp() {
+// external_flag = GetParam();
+// }
+// }
+// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool());
+//
+inline internal::ParamGenerator<bool> Bool() {
+ return Values(false, true);
+}
+
+# if GTEST_HAS_COMBINE
+// Combine() allows the user to combine two or more sequences to produce
+// values of a Cartesian product of those sequences' elements.
+//
+// Synopsis:
+// Combine(gen1, gen2, ..., genN)
+// - returns a generator producing sequences with elements coming from
+// the Cartesian product of elements from the sequences generated by
+// gen1, gen2, ..., genN. The sequence elements will have a type of
+// tuple<T1, T2, ..., TN> where T1, T2, ..., TN are the types
+// of elements from sequences produces by gen1, gen2, ..., genN.
+//
+// Combine can have up to 10 arguments. This number is currently limited
+// by the maximum number of elements in the tuple implementation used by Google
+// Test.
+//
+// Example:
+//
+// This will instantiate tests in test case AnimalTest each one with
+// the parameter values tuple("cat", BLACK), tuple("cat", WHITE),
+// tuple("dog", BLACK), and tuple("dog", WHITE):
+//
+// enum Color { BLACK, GRAY, WHITE };
+// class AnimalTest
+// : public testing::TestWithParam<tuple<const char*, Color> > {...};
+//
+// TEST_P(AnimalTest, AnimalLooksNice) {...}
+//
+// INSTANTIATE_TEST_CASE_P(AnimalVariations, AnimalTest,
+// Combine(Values("cat", "dog"),
+// Values(BLACK, WHITE)));
+//
+// This will instantiate tests in FlagDependentTest with all variations of two
+// Boolean flags:
+//
+// class FlagDependentTest
+// : public testing::TestWithParam<tuple<bool, bool> > {
+// virtual void SetUp() {
+// // Assigns external_flag_1 and external_flag_2 values from the tuple.
+// tie(external_flag_1, external_flag_2) = GetParam();
+// }
+// };
+//
+// TEST_P(FlagDependentTest, TestFeature1) {
+// // Test your code using external_flag_1 and external_flag_2 here.
+// }
+// INSTANTIATE_TEST_CASE_P(TwoBoolSequence, FlagDependentTest,
+// Combine(Bool(), Bool()));
+//
+template <typename Generator1, typename Generator2>
+internal::CartesianProductHolder2<Generator1, Generator2> Combine(
+ const Generator1& g1, const Generator2& g2) {
+ return internal::CartesianProductHolder2<Generator1, Generator2>(
+ g1, g2);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3>
+internal::CartesianProductHolder3<Generator1, Generator2, Generator3> Combine(
+ const Generator1& g1, const Generator2& g2, const Generator3& g3) {
+ return internal::CartesianProductHolder3<Generator1, Generator2, Generator3>(
+ g1, g2, g3);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+ typename Generator4>
+internal::CartesianProductHolder4<Generator1, Generator2, Generator3,
+ Generator4> Combine(
+ const Generator1& g1, const Generator2& g2, const Generator3& g3,
+ const Generator4& g4) {
+ return internal::CartesianProductHolder4<Generator1, Generator2, Generator3,
+ Generator4>(
+ g1, g2, g3, g4);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+ typename Generator4, typename Generator5>
+internal::CartesianProductHolder5<Generator1, Generator2, Generator3,
+ Generator4, Generator5> Combine(
+ const Generator1& g1, const Generator2& g2, const Generator3& g3,
+ const Generator4& g4, const Generator5& g5) {
+ return internal::CartesianProductHolder5<Generator1, Generator2, Generator3,
+ Generator4, Generator5>(
+ g1, g2, g3, g4, g5);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+ typename Generator4, typename Generator5, typename Generator6>
+internal::CartesianProductHolder6<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6> Combine(
+ const Generator1& g1, const Generator2& g2, const Generator3& g3,
+ const Generator4& g4, const Generator5& g5, const Generator6& g6) {
+ return internal::CartesianProductHolder6<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6>(
+ g1, g2, g3, g4, g5, g6);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+ typename Generator4, typename Generator5, typename Generator6,
+ typename Generator7>
+internal::CartesianProductHolder7<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6, Generator7> Combine(
+ const Generator1& g1, const Generator2& g2, const Generator3& g3,
+ const Generator4& g4, const Generator5& g5, const Generator6& g6,
+ const Generator7& g7) {
+ return internal::CartesianProductHolder7<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6, Generator7>(
+ g1, g2, g3, g4, g5, g6, g7);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+ typename Generator4, typename Generator5, typename Generator6,
+ typename Generator7, typename Generator8>
+internal::CartesianProductHolder8<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6, Generator7, Generator8> Combine(
+ const Generator1& g1, const Generator2& g2, const Generator3& g3,
+ const Generator4& g4, const Generator5& g5, const Generator6& g6,
+ const Generator7& g7, const Generator8& g8) {
+ return internal::CartesianProductHolder8<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6, Generator7, Generator8>(
+ g1, g2, g3, g4, g5, g6, g7, g8);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+ typename Generator4, typename Generator5, typename Generator6,
+ typename Generator7, typename Generator8, typename Generator9>
+internal::CartesianProductHolder9<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6, Generator7, Generator8,
+ Generator9> Combine(
+ const Generator1& g1, const Generator2& g2, const Generator3& g3,
+ const Generator4& g4, const Generator5& g5, const Generator6& g6,
+ const Generator7& g7, const Generator8& g8, const Generator9& g9) {
+ return internal::CartesianProductHolder9<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6, Generator7, Generator8, Generator9>(
+ g1, g2, g3, g4, g5, g6, g7, g8, g9);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+ typename Generator4, typename Generator5, typename Generator6,
+ typename Generator7, typename Generator8, typename Generator9,
+ typename Generator10>
+internal::CartesianProductHolder10<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6, Generator7, Generator8, Generator9,
+ Generator10> Combine(
+ const Generator1& g1, const Generator2& g2, const Generator3& g3,
+ const Generator4& g4, const Generator5& g5, const Generator6& g6,
+ const Generator7& g7, const Generator8& g8, const Generator9& g9,
+ const Generator10& g10) {
+ return internal::CartesianProductHolder10<Generator1, Generator2, Generator3,
+ Generator4, Generator5, Generator6, Generator7, Generator8, Generator9,
+ Generator10>(
+ g1, g2, g3, g4, g5, g6, g7, g8, g9, g10);
+}
+# endif // GTEST_HAS_COMBINE
+
+
+
+# define TEST_P(test_case_name, test_name) \
+ class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
+ : public test_case_name { \
+ public: \
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \
+ virtual void TestBody(); \
+ private: \
+ static int AddToRegistry() { \
+ ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
+ GetTestCasePatternHolder<test_case_name>(\
+ #test_case_name, __FILE__, __LINE__)->AddTestPattern(\
+ #test_case_name, \
+ #test_name, \
+ new ::testing::internal::TestMetaFactory< \
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>()); \
+ return 0; \
+ } \
+ static int gtest_registering_dummy_; \
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(\
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \
+ }; \
+ int GTEST_TEST_CLASS_NAME_(test_case_name, \
+ test_name)::gtest_registering_dummy_ = \
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \
+ void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
+
+# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \
+ ::testing::internal::ParamGenerator<test_case_name::ParamType> \
+ gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \
+ int gtest_##prefix##test_case_name##_dummy_ = \
+ ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
+ GetTestCasePatternHolder<test_case_name>(\
+ #test_case_name, __FILE__, __LINE__)->AddTestCaseInstantiation(\
+ #prefix, \
+ &gtest_##prefix##test_case_name##_EvalGenerator_, \
+ __FILE__, __LINE__)
+
+} // namespace testing
+
+#endif // GTEST_HAS_PARAM_TEST
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
diff --git a/extern/gtest/include/gtest/gtest-printers.h b/extern/gtest/include/gtest/gtest-printers.h
new file mode 100644
index 00000000000..0639d9f5869
--- /dev/null
+++ b/extern/gtest/include/gtest/gtest-printers.h
@@ -0,0 +1,855 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Test - The Google C++ Testing Framework
+//
+// This file implements a universal value printer that can print a
+// value of any type T:
+//
+// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
+//
+// A user can teach this function how to print a class type T by
+// defining either operator<<() or PrintTo() in the namespace that
+// defines T. More specifically, the FIRST defined function in the
+// following list will be used (assuming T is defined in namespace
+// foo):
+//
+// 1. foo::PrintTo(const T&, ostream*)
+// 2. operator<<(ostream&, const T&) defined in either foo or the
+// global namespace.
+//
+// If none of the above is defined, it will print the debug string of
+// the value if it is a protocol buffer, or print the raw bytes in the
+// value otherwise.
+//
+// To aid debugging: when T is a reference type, the address of the
+// value is also printed; when T is a (const) char pointer, both the
+// pointer value and the NUL-terminated string it points to are
+// printed.
+//
+// We also provide some convenient wrappers:
+//
+// // Prints a value to a string. For a (const or not) char
+// // pointer, the NUL-terminated string (but not the pointer) is
+// // printed.
+// std::string ::testing::PrintToString(const T& value);
+//
+// // Prints a value tersely: for a reference type, the referenced
+// // value (but not the address) is printed; for a (const or not) char
+// // pointer, the NUL-terminated string (but not the pointer) is
+// // printed.
+// void ::testing::internal::UniversalTersePrint(const T& value, ostream*);
+//
+// // Prints value using the type inferred by the compiler. The difference
+// // from UniversalTersePrint() is that this function prints both the
+// // pointer and the NUL-terminated string for a (const or not) char pointer.
+// void ::testing::internal::UniversalPrint(const T& value, ostream*);
+//
+// // Prints the fields of a tuple tersely to a string vector, one
+// // element for each field. Tuple support must be enabled in
+// // gtest-port.h.
+// std::vector<string> UniversalTersePrintTupleFieldsToStrings(
+// const Tuple& value);
+//
+// Known limitation:
+//
+// The print primitives print the elements of an STL-style container
+// using the compiler-inferred type of *iter where iter is a
+// const_iterator of the container. When const_iterator is an input
+// iterator but not a forward iterator, this inferred type may not
+// match value_type, and the print output may be incorrect. In
+// practice, this is rarely a problem as for most containers
+// const_iterator is a forward iterator. We'll fix this if there's an
+// actual need for it. Note that this fix cannot rely on value_type
+// being defined as many user-defined container types don't have
+// value_type.
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
+#define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
+
+#include <ostream> // NOLINT
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+#include "gtest/internal/gtest-port.h"
+#include "gtest/internal/gtest-internal.h"
+
+namespace testing {
+
+// Definitions in the 'internal' and 'internal2' name spaces are
+// subject to change without notice. DO NOT USE THEM IN USER CODE!
+namespace internal2 {
+
+// Prints the given number of bytes in the given object to the given
+// ostream.
+GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes,
+ size_t count,
+ ::std::ostream* os);
+
+// For selecting which printer to use when a given type has neither <<
+// nor PrintTo().
+enum TypeKind {
+ kProtobuf, // a protobuf type
+ kConvertibleToInteger, // a type implicitly convertible to BiggestInt
+ // (e.g. a named or unnamed enum type)
+ kOtherType // anything else
+};
+
+// TypeWithoutFormatter<T, kTypeKind>::PrintValue(value, os) is called
+// by the universal printer to print a value of type T when neither
+// operator<< nor PrintTo() is defined for T, where kTypeKind is the
+// "kind" of T as defined by enum TypeKind.
+template <typename T, TypeKind kTypeKind>
+class TypeWithoutFormatter {
+ public:
+ // This default version is called when kTypeKind is kOtherType.
+ static void PrintValue(const T& value, ::std::ostream* os) {
+ PrintBytesInObjectTo(reinterpret_cast<const unsigned char*>(&value),
+ sizeof(value), os);
+ }
+};
+
+// We print a protobuf using its ShortDebugString() when the string
+// doesn't exceed this many characters; otherwise we print it using
+// DebugString() for better readability.
+const size_t kProtobufOneLinerMaxLength = 50;
+
+template <typename T>
+class TypeWithoutFormatter<T, kProtobuf> {
+ public:
+ static void PrintValue(const T& value, ::std::ostream* os) {
+ const ::testing::internal::string short_str = value.ShortDebugString();
+ const ::testing::internal::string pretty_str =
+ short_str.length() <= kProtobufOneLinerMaxLength ?
+ short_str : ("\n" + value.DebugString());
+ *os << ("<" + pretty_str + ">");
+ }
+};
+
+template <typename T>
+class TypeWithoutFormatter<T, kConvertibleToInteger> {
+ public:
+ // Since T has no << operator or PrintTo() but can be implicitly
+ // converted to BiggestInt, we print it as a BiggestInt.
+ //
+ // Most likely T is an enum type (either named or unnamed), in which
+ // case printing it as an integer is the desired behavior. In case
+ // T is not an enum, printing it as an integer is the best we can do
+ // given that it has no user-defined printer.
+ static void PrintValue(const T& value, ::std::ostream* os) {
+ const internal::BiggestInt kBigInt = value;
+ *os << kBigInt;
+ }
+};
+
+// Prints the given value to the given ostream. If the value is a
+// protocol message, its debug string is printed; if it's an enum or
+// of a type implicitly convertible to BiggestInt, it's printed as an
+// integer; otherwise the bytes in the value are printed. This is
+// what UniversalPrinter<T>::Print() does when it knows nothing about
+// type T and T has neither << operator nor PrintTo().
+//
+// A user can override this behavior for a class type Foo by defining
+// a << operator in the namespace where Foo is defined.
+//
+// We put this operator in namespace 'internal2' instead of 'internal'
+// to simplify the implementation, as much code in 'internal' needs to
+// use << in STL, which would conflict with our own << were it defined
+// in 'internal'.
+//
+// Note that this operator<< takes a generic std::basic_ostream<Char,
+// CharTraits> type instead of the more restricted std::ostream. If
+// we define it to take an std::ostream instead, we'll get an
+// "ambiguous overloads" compiler error when trying to print a type
+// Foo that supports streaming to std::basic_ostream<Char,
+// CharTraits>, as the compiler cannot tell whether
+// operator<<(std::ostream&, const T&) or
+// operator<<(std::basic_stream<Char, CharTraits>, const Foo&) is more
+// specific.
+template <typename Char, typename CharTraits, typename T>
+::std::basic_ostream<Char, CharTraits>& operator<<(
+ ::std::basic_ostream<Char, CharTraits>& os, const T& x) {
+ TypeWithoutFormatter<T,
+ (internal::IsAProtocolMessage<T>::value ? kProtobuf :
+ internal::ImplicitlyConvertible<const T&, internal::BiggestInt>::value ?
+ kConvertibleToInteger : kOtherType)>::PrintValue(x, &os);
+ return os;
+}
+
+} // namespace internal2
+} // namespace testing
+
+// This namespace MUST NOT BE NESTED IN ::testing, or the name look-up
+// magic needed for implementing UniversalPrinter won't work.
+namespace testing_internal {
+
+// Used to print a value that is not an STL-style container when the
+// user doesn't define PrintTo() for it.
+template <typename T>
+void DefaultPrintNonContainerTo(const T& value, ::std::ostream* os) {
+ // With the following statement, during unqualified name lookup,
+ // testing::internal2::operator<< appears as if it was declared in
+ // the nearest enclosing namespace that contains both
+ // ::testing_internal and ::testing::internal2, i.e. the global
+ // namespace. For more details, refer to the C++ Standard section
+ // 7.3.4-1 [namespace.udir]. This allows us to fall back onto
+ // testing::internal2::operator<< in case T doesn't come with a <<
+ // operator.
+ //
+ // We cannot write 'using ::testing::internal2::operator<<;', which
+ // gcc 3.3 fails to compile due to a compiler bug.
+ using namespace ::testing::internal2; // NOLINT
+
+ // Assuming T is defined in namespace foo, in the next statement,
+ // the compiler will consider all of:
+ //
+ // 1. foo::operator<< (thanks to Koenig look-up),
+ // 2. ::operator<< (as the current namespace is enclosed in ::),
+ // 3. testing::internal2::operator<< (thanks to the using statement above).
+ //
+ // The operator<< whose type matches T best will be picked.
+ //
+ // We deliberately allow #2 to be a candidate, as sometimes it's
+ // impossible to define #1 (e.g. when foo is ::std, defining
+ // anything in it is undefined behavior unless you are a compiler
+ // vendor.).
+ *os << value;
+}
+
+} // namespace testing_internal
+
+namespace testing {
+namespace internal {
+
+// UniversalPrinter<T>::Print(value, ostream_ptr) prints the given
+// value to the given ostream. The caller must ensure that
+// 'ostream_ptr' is not NULL, or the behavior is undefined.
+//
+// We define UniversalPrinter as a class template (as opposed to a
+// function template), as we need to partially specialize it for
+// reference types, which cannot be done with function templates.
+template <typename T>
+class UniversalPrinter;
+
+template <typename T>
+void UniversalPrint(const T& value, ::std::ostream* os);
+
+// Used to print an STL-style container when the user doesn't define
+// a PrintTo() for it.
+template <typename C>
+void DefaultPrintTo(IsContainer /* dummy */,
+ false_type /* is not a pointer */,
+ const C& container, ::std::ostream* os) {
+ const size_t kMaxCount = 32; // The maximum number of elements to print.
+ *os << '{';
+ size_t count = 0;
+ for (typename C::const_iterator it = container.begin();
+ it != container.end(); ++it, ++count) {
+ if (count > 0) {
+ *os << ',';
+ if (count == kMaxCount) { // Enough has been printed.
+ *os << " ...";
+ break;
+ }
+ }
+ *os << ' ';
+ // We cannot call PrintTo(*it, os) here as PrintTo() doesn't
+ // handle *it being a native array.
+ internal::UniversalPrint(*it, os);
+ }
+
+ if (count > 0) {
+ *os << ' ';
+ }
+ *os << '}';
+}
+
+// Used to print a pointer that is neither a char pointer nor a member
+// pointer, when the user doesn't define PrintTo() for it. (A member
+// variable pointer or member function pointer doesn't really point to
+// a location in the address space. Their representation is
+// implementation-defined. Therefore they will be printed as raw
+// bytes.)
+template <typename T>
+void DefaultPrintTo(IsNotContainer /* dummy */,
+ true_type /* is a pointer */,
+ T* p, ::std::ostream* os) {
+ if (p == NULL) {
+ *os << "NULL";
+ } else {
+ // C++ doesn't allow casting from a function pointer to any object
+ // pointer.
+ //
+ // IsTrue() silences warnings: "Condition is always true",
+ // "unreachable code".
+ if (IsTrue(ImplicitlyConvertible<T*, const void*>::value)) {
+ // T is not a function type. We just call << to print p,
+ // relying on ADL to pick up user-defined << for their pointer
+ // types, if any.
+ *os << p;
+ } else {
+ // T is a function type, so '*os << p' doesn't do what we want
+ // (it just prints p as bool). We want to print p as a const
+ // void*. However, we cannot cast it to const void* directly,
+ // even using reinterpret_cast, as earlier versions of gcc
+ // (e.g. 3.4.5) cannot compile the cast when p is a function
+ // pointer. Casting to UInt64 first solves the problem.
+ *os << reinterpret_cast<const void*>(
+ reinterpret_cast<internal::UInt64>(p));
+ }
+ }
+}
+
+// Used to print a non-container, non-pointer value when the user
+// doesn't define PrintTo() for it.
+template <typename T>
+void DefaultPrintTo(IsNotContainer /* dummy */,
+ false_type /* is not a pointer */,
+ const T& value, ::std::ostream* os) {
+ ::testing_internal::DefaultPrintNonContainerTo(value, os);
+}
+
+// Prints the given value using the << operator if it has one;
+// otherwise prints the bytes in it. This is what
+// UniversalPrinter<T>::Print() does when PrintTo() is not specialized
+// or overloaded for type T.
+//
+// A user can override this behavior for a class type Foo by defining
+// an overload of PrintTo() in the namespace where Foo is defined. We
+// give the user this option as sometimes defining a << operator for
+// Foo is not desirable (e.g. the coding style may prevent doing it,
+// or there is already a << operator but it doesn't do what the user
+// wants).
+template <typename T>
+void PrintTo(const T& value, ::std::ostream* os) {
+ // DefaultPrintTo() is overloaded. The type of its first two
+ // arguments determine which version will be picked. If T is an
+ // STL-style container, the version for container will be called; if
+ // T is a pointer, the pointer version will be called; otherwise the
+ // generic version will be called.
+ //
+ // Note that we check for container types here, prior to we check
+ // for protocol message types in our operator<<. The rationale is:
+ //
+ // For protocol messages, we want to give people a chance to
+ // override Google Mock's format by defining a PrintTo() or
+ // operator<<. For STL containers, other formats can be
+ // incompatible with Google Mock's format for the container
+ // elements; therefore we check for container types here to ensure
+ // that our format is used.
+ //
+ // The second argument of DefaultPrintTo() is needed to bypass a bug
+ // in Symbian's C++ compiler that prevents it from picking the right
+ // overload between:
+ //
+ // PrintTo(const T& x, ...);
+ // PrintTo(T* x, ...);
+ DefaultPrintTo(IsContainerTest<T>(0), is_pointer<T>(), value, os);
+}
+
+// The following list of PrintTo() overloads tells
+// UniversalPrinter<T>::Print() how to print standard types (built-in
+// types, strings, plain arrays, and pointers).
+
+// Overloads for various char types.
+GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os);
+GTEST_API_ void PrintTo(signed char c, ::std::ostream* os);
+inline void PrintTo(char c, ::std::ostream* os) {
+ // When printing a plain char, we always treat it as unsigned. This
+ // way, the output won't be affected by whether the compiler thinks
+ // char is signed or not.
+ PrintTo(static_cast<unsigned char>(c), os);
+}
+
+// Overloads for other simple built-in types.
+inline void PrintTo(bool x, ::std::ostream* os) {
+ *os << (x ? "true" : "false");
+}
+
+// Overload for wchar_t type.
+// Prints a wchar_t as a symbol if it is printable or as its internal
+// code otherwise and also as its decimal code (except for L'\0').
+// The L'\0' char is printed as "L'\\0'". The decimal code is printed
+// as signed integer when wchar_t is implemented by the compiler
+// as a signed type and is printed as an unsigned integer when wchar_t
+// is implemented as an unsigned type.
+GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os);
+
+// Overloads for C strings.
+GTEST_API_ void PrintTo(const char* s, ::std::ostream* os);
+inline void PrintTo(char* s, ::std::ostream* os) {
+ PrintTo(ImplicitCast_<const char*>(s), os);
+}
+
+// signed/unsigned char is often used for representing binary data, so
+// we print pointers to it as void* to be safe.
+inline void PrintTo(const signed char* s, ::std::ostream* os) {
+ PrintTo(ImplicitCast_<const void*>(s), os);
+}
+inline void PrintTo(signed char* s, ::std::ostream* os) {
+ PrintTo(ImplicitCast_<const void*>(s), os);
+}
+inline void PrintTo(const unsigned char* s, ::std::ostream* os) {
+ PrintTo(ImplicitCast_<const void*>(s), os);
+}
+inline void PrintTo(unsigned char* s, ::std::ostream* os) {
+ PrintTo(ImplicitCast_<const void*>(s), os);
+}
+
+// MSVC can be configured to define wchar_t as a typedef of unsigned
+// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native
+// type. When wchar_t is a typedef, defining an overload for const
+// wchar_t* would cause unsigned short* be printed as a wide string,
+// possibly causing invalid memory accesses.
+#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
+// Overloads for wide C strings
+GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os);
+inline void PrintTo(wchar_t* s, ::std::ostream* os) {
+ PrintTo(ImplicitCast_<const wchar_t*>(s), os);
+}
+#endif
+
+// Overload for C arrays. Multi-dimensional arrays are printed
+// properly.
+
+// Prints the given number of elements in an array, without printing
+// the curly braces.
+template <typename T>
+void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) {
+ UniversalPrint(a[0], os);
+ for (size_t i = 1; i != count; i++) {
+ *os << ", ";
+ UniversalPrint(a[i], os);
+ }
+}
+
+// Overloads for ::string and ::std::string.
+#if GTEST_HAS_GLOBAL_STRING
+GTEST_API_ void PrintStringTo(const ::string&s, ::std::ostream* os);
+inline void PrintTo(const ::string& s, ::std::ostream* os) {
+ PrintStringTo(s, os);
+}
+#endif // GTEST_HAS_GLOBAL_STRING
+
+GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os);
+inline void PrintTo(const ::std::string& s, ::std::ostream* os) {
+ PrintStringTo(s, os);
+}
+
+// Overloads for ::wstring and ::std::wstring.
+#if GTEST_HAS_GLOBAL_WSTRING
+GTEST_API_ void PrintWideStringTo(const ::wstring&s, ::std::ostream* os);
+inline void PrintTo(const ::wstring& s, ::std::ostream* os) {
+ PrintWideStringTo(s, os);
+}
+#endif // GTEST_HAS_GLOBAL_WSTRING
+
+#if GTEST_HAS_STD_WSTRING
+GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os);
+inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) {
+ PrintWideStringTo(s, os);
+}
+#endif // GTEST_HAS_STD_WSTRING
+
+#if GTEST_HAS_TR1_TUPLE
+// Overload for ::std::tr1::tuple. Needed for printing function arguments,
+// which are packed as tuples.
+
+// Helper function for printing a tuple. T must be instantiated with
+// a tuple type.
+template <typename T>
+void PrintTupleTo(const T& t, ::std::ostream* os);
+
+// Overloaded PrintTo() for tuples of various arities. We support
+// tuples of up-to 10 fields. The following implementation works
+// regardless of whether tr1::tuple is implemented using the
+// non-standard variadic template feature or not.
+
+inline void PrintTo(const ::std::tr1::tuple<>& t, ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1>
+void PrintTo(const ::std::tr1::tuple<T1>& t, ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2>
+void PrintTo(const ::std::tr1::tuple<T1, T2>& t, ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3>& t, ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4>& t, ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5>& t,
+ ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6>& t,
+ ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7>& t,
+ ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8>& t,
+ ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9>& t,
+ ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+void PrintTo(
+ const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>& t,
+ ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+#endif // GTEST_HAS_TR1_TUPLE
+
+// Overload for std::pair.
+template <typename T1, typename T2>
+void PrintTo(const ::std::pair<T1, T2>& value, ::std::ostream* os) {
+ *os << '(';
+ // We cannot use UniversalPrint(value.first, os) here, as T1 may be
+ // a reference type. The same for printing value.second.
+ UniversalPrinter<T1>::Print(value.first, os);
+ *os << ", ";
+ UniversalPrinter<T2>::Print(value.second, os);
+ *os << ')';
+}
+
+// Implements printing a non-reference type T by letting the compiler
+// pick the right overload of PrintTo() for T.
+template <typename T>
+class UniversalPrinter {
+ public:
+ // MSVC warns about adding const to a function type, so we want to
+ // disable the warning.
+#ifdef _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4180) // Temporarily disables warning 4180.
+#endif // _MSC_VER
+
+ // Note: we deliberately don't call this PrintTo(), as that name
+ // conflicts with ::testing::internal::PrintTo in the body of the
+ // function.
+ static void Print(const T& value, ::std::ostream* os) {
+ // By default, ::testing::internal::PrintTo() is used for printing
+ // the value.
+ //
+ // Thanks to Koenig look-up, if T is a class and has its own
+ // PrintTo() function defined in its namespace, that function will
+ // be visible here. Since it is more specific than the generic ones
+ // in ::testing::internal, it will be picked by the compiler in the
+ // following statement - exactly what we want.
+ PrintTo(value, os);
+ }
+
+#ifdef _MSC_VER
+# pragma warning(pop) // Restores the warning state.
+#endif // _MSC_VER
+};
+
+// UniversalPrintArray(begin, len, os) prints an array of 'len'
+// elements, starting at address 'begin'.
+template <typename T>
+void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) {
+ if (len == 0) {
+ *os << "{}";
+ } else {
+ *os << "{ ";
+ const size_t kThreshold = 18;
+ const size_t kChunkSize = 8;
+ // If the array has more than kThreshold elements, we'll have to
+ // omit some details by printing only the first and the last
+ // kChunkSize elements.
+ // TODO(wan@google.com): let the user control the threshold using a flag.
+ if (len <= kThreshold) {
+ PrintRawArrayTo(begin, len, os);
+ } else {
+ PrintRawArrayTo(begin, kChunkSize, os);
+ *os << ", ..., ";
+ PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os);
+ }
+ *os << " }";
+ }
+}
+// This overload prints a (const) char array compactly.
+GTEST_API_ void UniversalPrintArray(
+ const char* begin, size_t len, ::std::ostream* os);
+
+// This overload prints a (const) wchar_t array compactly.
+GTEST_API_ void UniversalPrintArray(
+ const wchar_t* begin, size_t len, ::std::ostream* os);
+
+// Implements printing an array type T[N].
+template <typename T, size_t N>
+class UniversalPrinter<T[N]> {
+ public:
+ // Prints the given array, omitting some elements when there are too
+ // many.
+ static void Print(const T (&a)[N], ::std::ostream* os) {
+ UniversalPrintArray(a, N, os);
+ }
+};
+
+// Implements printing a reference type T&.
+template <typename T>
+class UniversalPrinter<T&> {
+ public:
+ // MSVC warns about adding const to a function type, so we want to
+ // disable the warning.
+#ifdef _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4180) // Temporarily disables warning 4180.
+#endif // _MSC_VER
+
+ static void Print(const T& value, ::std::ostream* os) {
+ // Prints the address of the value. We use reinterpret_cast here
+ // as static_cast doesn't compile when T is a function type.
+ *os << "@" << reinterpret_cast<const void*>(&value) << " ";
+
+ // Then prints the value itself.
+ UniversalPrint(value, os);
+ }
+
+#ifdef _MSC_VER
+# pragma warning(pop) // Restores the warning state.
+#endif // _MSC_VER
+};
+
+// Prints a value tersely: for a reference type, the referenced value
+// (but not the address) is printed; for a (const) char pointer, the
+// NUL-terminated string (but not the pointer) is printed.
+
+template <typename T>
+class UniversalTersePrinter {
+ public:
+ static void Print(const T& value, ::std::ostream* os) {
+ UniversalPrint(value, os);
+ }
+};
+template <typename T>
+class UniversalTersePrinter<T&> {
+ public:
+ static void Print(const T& value, ::std::ostream* os) {
+ UniversalPrint(value, os);
+ }
+};
+template <typename T, size_t N>
+class UniversalTersePrinter<T[N]> {
+ public:
+ static void Print(const T (&value)[N], ::std::ostream* os) {
+ UniversalPrinter<T[N]>::Print(value, os);
+ }
+};
+template <>
+class UniversalTersePrinter<const char*> {
+ public:
+ static void Print(const char* str, ::std::ostream* os) {
+ if (str == NULL) {
+ *os << "NULL";
+ } else {
+ UniversalPrint(string(str), os);
+ }
+ }
+};
+template <>
+class UniversalTersePrinter<char*> {
+ public:
+ static void Print(char* str, ::std::ostream* os) {
+ UniversalTersePrinter<const char*>::Print(str, os);
+ }
+};
+
+#if GTEST_HAS_STD_WSTRING
+template <>
+class UniversalTersePrinter<const wchar_t*> {
+ public:
+ static void Print(const wchar_t* str, ::std::ostream* os) {
+ if (str == NULL) {
+ *os << "NULL";
+ } else {
+ UniversalPrint(::std::wstring(str), os);
+ }
+ }
+};
+#endif
+
+template <>
+class UniversalTersePrinter<wchar_t*> {
+ public:
+ static void Print(wchar_t* str, ::std::ostream* os) {
+ UniversalTersePrinter<const wchar_t*>::Print(str, os);
+ }
+};
+
+template <typename T>
+void UniversalTersePrint(const T& value, ::std::ostream* os) {
+ UniversalTersePrinter<T>::Print(value, os);
+}
+
+// Prints a value using the type inferred by the compiler. The
+// difference between this and UniversalTersePrint() is that for a
+// (const) char pointer, this prints both the pointer and the
+// NUL-terminated string.
+template <typename T>
+void UniversalPrint(const T& value, ::std::ostream* os) {
+ // A workarond for the bug in VC++ 7.1 that prevents us from instantiating
+ // UniversalPrinter with T directly.
+ typedef T T1;
+ UniversalPrinter<T1>::Print(value, os);
+}
+
+#if GTEST_HAS_TR1_TUPLE
+typedef ::std::vector<string> Strings;
+
+// This helper template allows PrintTo() for tuples and
+// UniversalTersePrintTupleFieldsToStrings() to be defined by
+// induction on the number of tuple fields. The idea is that
+// TuplePrefixPrinter<N>::PrintPrefixTo(t, os) prints the first N
+// fields in tuple t, and can be defined in terms of
+// TuplePrefixPrinter<N - 1>.
+
+// The inductive case.
+template <size_t N>
+struct TuplePrefixPrinter {
+ // Prints the first N fields of a tuple.
+ template <typename Tuple>
+ static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
+ TuplePrefixPrinter<N - 1>::PrintPrefixTo(t, os);
+ *os << ", ";
+ UniversalPrinter<typename ::std::tr1::tuple_element<N - 1, Tuple>::type>
+ ::Print(::std::tr1::get<N - 1>(t), os);
+ }
+
+ // Tersely prints the first N fields of a tuple to a string vector,
+ // one element for each field.
+ template <typename Tuple>
+ static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) {
+ TuplePrefixPrinter<N - 1>::TersePrintPrefixToStrings(t, strings);
+ ::std::stringstream ss;
+ UniversalTersePrint(::std::tr1::get<N - 1>(t), &ss);
+ strings->push_back(ss.str());
+ }
+};
+
+// Base cases.
+template <>
+struct TuplePrefixPrinter<0> {
+ template <typename Tuple>
+ static void PrintPrefixTo(const Tuple&, ::std::ostream*) {}
+
+ template <typename Tuple>
+ static void TersePrintPrefixToStrings(const Tuple&, Strings*) {}
+};
+// We have to specialize the entire TuplePrefixPrinter<> class
+// template here, even though the definition of
+// TersePrintPrefixToStrings() is the same as the generic version, as
+// Embarcadero (formerly CodeGear, formerly Borland) C++ doesn't
+// support specializing a method template of a class template.
+template <>
+struct TuplePrefixPrinter<1> {
+ template <typename Tuple>
+ static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
+ UniversalPrinter<typename ::std::tr1::tuple_element<0, Tuple>::type>::
+ Print(::std::tr1::get<0>(t), os);
+ }
+
+ template <typename Tuple>
+ static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) {
+ ::std::stringstream ss;
+ UniversalTersePrint(::std::tr1::get<0>(t), &ss);
+ strings->push_back(ss.str());
+ }
+};
+
+// Helper function for printing a tuple. T must be instantiated with
+// a tuple type.
+template <typename T>
+void PrintTupleTo(const T& t, ::std::ostream* os) {
+ *os << "(";
+ TuplePrefixPrinter< ::std::tr1::tuple_size<T>::value>::
+ PrintPrefixTo(t, os);
+ *os << ")";
+}
+
+// Prints the fields of a tuple tersely to a string vector, one
+// element for each field. See the comment before
+// UniversalTersePrint() for how we define "tersely".
+template <typename Tuple>
+Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) {
+ Strings result;
+ TuplePrefixPrinter< ::std::tr1::tuple_size<Tuple>::value>::
+ TersePrintPrefixToStrings(value, &result);
+ return result;
+}
+#endif // GTEST_HAS_TR1_TUPLE
+
+} // namespace internal
+
+template <typename T>
+::std::string PrintToString(const T& value) {
+ ::std::stringstream ss;
+ internal::UniversalTersePrinter<T>::Print(value, &ss);
+ return ss.str();
+}
+
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
diff --git a/extern/gtest/include/gtest/gtest-spi.h b/extern/gtest/include/gtest/gtest-spi.h
new file mode 100644
index 00000000000..f63fa9a1b2a
--- /dev/null
+++ b/extern/gtest/include/gtest/gtest-spi.h
@@ -0,0 +1,232 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+//
+// Utilities for testing Google Test itself and code that uses Google Test
+// (e.g. frameworks built on top of Google Test).
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_
+#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_
+
+#include "gtest/gtest.h"
+
+namespace testing {
+
+// This helper class can be used to mock out Google Test failure reporting
+// so that we can test Google Test or code that builds on Google Test.
+//
+// An object of this class appends a TestPartResult object to the
+// TestPartResultArray object given in the constructor whenever a Google Test
+// failure is reported. It can either intercept only failures that are
+// generated in the same thread that created this object or it can intercept
+// all generated failures. The scope of this mock object can be controlled with
+// the second argument to the two arguments constructor.
+class GTEST_API_ ScopedFakeTestPartResultReporter
+ : public TestPartResultReporterInterface {
+ public:
+ // The two possible mocking modes of this object.
+ enum InterceptMode {
+ INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures.
+ INTERCEPT_ALL_THREADS // Intercepts all failures.
+ };
+
+ // The c'tor sets this object as the test part result reporter used
+ // by Google Test. The 'result' parameter specifies where to report the
+ // results. This reporter will only catch failures generated in the current
+ // thread. DEPRECATED
+ explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result);
+
+ // Same as above, but you can choose the interception scope of this object.
+ ScopedFakeTestPartResultReporter(InterceptMode intercept_mode,
+ TestPartResultArray* result);
+
+ // The d'tor restores the previous test part result reporter.
+ virtual ~ScopedFakeTestPartResultReporter();
+
+ // Appends the TestPartResult object to the TestPartResultArray
+ // received in the constructor.
+ //
+ // This method is from the TestPartResultReporterInterface
+ // interface.
+ virtual void ReportTestPartResult(const TestPartResult& result);
+ private:
+ void Init();
+
+ const InterceptMode intercept_mode_;
+ TestPartResultReporterInterface* old_reporter_;
+ TestPartResultArray* const result_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter);
+};
+
+namespace internal {
+
+// A helper class for implementing EXPECT_FATAL_FAILURE() and
+// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given
+// TestPartResultArray contains exactly one failure that has the given
+// type and contains the given substring. If that's not the case, a
+// non-fatal failure will be generated.
+class GTEST_API_ SingleFailureChecker {
+ public:
+ // The constructor remembers the arguments.
+ SingleFailureChecker(const TestPartResultArray* results,
+ TestPartResult::Type type,
+ const string& substr);
+ ~SingleFailureChecker();
+ private:
+ const TestPartResultArray* const results_;
+ const TestPartResult::Type type_;
+ const string substr_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker);
+};
+
+} // namespace internal
+
+} // namespace testing
+
+// A set of macros for testing Google Test assertions or code that's expected
+// to generate Google Test fatal failures. It verifies that the given
+// statement will cause exactly one fatal Google Test failure with 'substr'
+// being part of the failure message.
+//
+// There are two different versions of this macro. EXPECT_FATAL_FAILURE only
+// affects and considers failures generated in the current thread and
+// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
+//
+// The verification of the assertion is done correctly even when the statement
+// throws an exception or aborts the current function.
+//
+// Known restrictions:
+// - 'statement' cannot reference local non-static variables or
+// non-static members of the current object.
+// - 'statement' cannot return a value.
+// - You cannot stream a failure message to this macro.
+//
+// Note that even though the implementations of the following two
+// macros are much alike, we cannot refactor them to use a common
+// helper macro, due to some peculiarity in how the preprocessor
+// works. The AcceptsMacroThatExpandsToUnprotectedComma test in
+// gtest_unittest.cc will fail to compile if we do that.
+#define EXPECT_FATAL_FAILURE(statement, substr) \
+ do { \
+ class GTestExpectFatalFailureHelper {\
+ public:\
+ static void Execute() { statement; }\
+ };\
+ ::testing::TestPartResultArray gtest_failures;\
+ ::testing::internal::SingleFailureChecker gtest_checker(\
+ &gtest_failures, ::testing::TestPartResult::kFatalFailure, (substr));\
+ {\
+ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+ ::testing::ScopedFakeTestPartResultReporter:: \
+ INTERCEPT_ONLY_CURRENT_THREAD, &gtest_failures);\
+ GTestExpectFatalFailureHelper::Execute();\
+ }\
+ } while (::testing::internal::AlwaysFalse())
+
+#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
+ do { \
+ class GTestExpectFatalFailureHelper {\
+ public:\
+ static void Execute() { statement; }\
+ };\
+ ::testing::TestPartResultArray gtest_failures;\
+ ::testing::internal::SingleFailureChecker gtest_checker(\
+ &gtest_failures, ::testing::TestPartResult::kFatalFailure, (substr));\
+ {\
+ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+ ::testing::ScopedFakeTestPartResultReporter:: \
+ INTERCEPT_ALL_THREADS, &gtest_failures);\
+ GTestExpectFatalFailureHelper::Execute();\
+ }\
+ } while (::testing::internal::AlwaysFalse())
+
+// A macro for testing Google Test assertions or code that's expected to
+// generate Google Test non-fatal failures. It asserts that the given
+// statement will cause exactly one non-fatal Google Test failure with 'substr'
+// being part of the failure message.
+//
+// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only
+// affects and considers failures generated in the current thread and
+// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
+//
+// 'statement' is allowed to reference local variables and members of
+// the current object.
+//
+// The verification of the assertion is done correctly even when the statement
+// throws an exception or aborts the current function.
+//
+// Known restrictions:
+// - You cannot stream a failure message to this macro.
+//
+// Note that even though the implementations of the following two
+// macros are much alike, we cannot refactor them to use a common
+// helper macro, due to some peculiarity in how the preprocessor
+// works. If we do that, the code won't compile when the user gives
+// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that
+// expands to code containing an unprotected comma. The
+// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc
+// catches that.
+//
+// For the same reason, we have to write
+// if (::testing::internal::AlwaysTrue()) { statement; }
+// instead of
+// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
+// to avoid an MSVC warning on unreachable code.
+#define EXPECT_NONFATAL_FAILURE(statement, substr) \
+ do {\
+ ::testing::TestPartResultArray gtest_failures;\
+ ::testing::internal::SingleFailureChecker gtest_checker(\
+ &gtest_failures, ::testing::TestPartResult::kNonFatalFailure, \
+ (substr));\
+ {\
+ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+ ::testing::ScopedFakeTestPartResultReporter:: \
+ INTERCEPT_ONLY_CURRENT_THREAD, &gtest_failures);\
+ if (::testing::internal::AlwaysTrue()) { statement; }\
+ }\
+ } while (::testing::internal::AlwaysFalse())
+
+#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
+ do {\
+ ::testing::TestPartResultArray gtest_failures;\
+ ::testing::internal::SingleFailureChecker gtest_checker(\
+ &gtest_failures, ::testing::TestPartResult::kNonFatalFailure, \
+ (substr));\
+ {\
+ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+ ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \
+ &gtest_failures);\
+ if (::testing::internal::AlwaysTrue()) { statement; }\
+ }\
+ } while (::testing::internal::AlwaysFalse())
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_
diff --git a/extern/gtest/include/gtest/gtest-test-part.h b/extern/gtest/include/gtest/gtest-test-part.h
new file mode 100644
index 00000000000..77eb844839d
--- /dev/null
+++ b/extern/gtest/include/gtest/gtest-test-part.h
@@ -0,0 +1,179 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: mheule@google.com (Markus Heule)
+//
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
+#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
+
+#include <iosfwd>
+#include <vector>
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-string.h"
+
+namespace testing {
+
+// A copyable object representing the result of a test part (i.e. an
+// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()).
+//
+// Don't inherit from TestPartResult as its destructor is not virtual.
+class GTEST_API_ TestPartResult {
+ public:
+ // The possible outcomes of a test part (i.e. an assertion or an
+ // explicit SUCCEED(), FAIL(), or ADD_FAILURE()).
+ enum Type {
+ kSuccess, // Succeeded.
+ kNonFatalFailure, // Failed but the test can continue.
+ kFatalFailure // Failed and the test should be terminated.
+ };
+
+ // C'tor. TestPartResult does NOT have a default constructor.
+ // Always use this constructor (with parameters) to create a
+ // TestPartResult object.
+ TestPartResult(Type a_type,
+ const char* a_file_name,
+ int a_line_number,
+ const char* a_message)
+ : type_(a_type),
+ file_name_(a_file_name == NULL ? "" : a_file_name),
+ line_number_(a_line_number),
+ summary_(ExtractSummary(a_message)),
+ message_(a_message) {
+ }
+
+ // Gets the outcome of the test part.
+ Type type() const { return type_; }
+
+ // Gets the name of the source file where the test part took place, or
+ // NULL if it's unknown.
+ const char* file_name() const {
+ return file_name_.empty() ? NULL : file_name_.c_str();
+ }
+
+ // Gets the line in the source file where the test part took place,
+ // or -1 if it's unknown.
+ int line_number() const { return line_number_; }
+
+ // Gets the summary of the failure message.
+ const char* summary() const { return summary_.c_str(); }
+
+ // Gets the message associated with the test part.
+ const char* message() const { return message_.c_str(); }
+
+ // Returns true iff the test part passed.
+ bool passed() const { return type_ == kSuccess; }
+
+ // Returns true iff the test part failed.
+ bool failed() const { return type_ != kSuccess; }
+
+ // Returns true iff the test part non-fatally failed.
+ bool nonfatally_failed() const { return type_ == kNonFatalFailure; }
+
+ // Returns true iff the test part fatally failed.
+ bool fatally_failed() const { return type_ == kFatalFailure; }
+
+ private:
+ Type type_;
+
+ // Gets the summary of the failure message by omitting the stack
+ // trace in it.
+ static std::string ExtractSummary(const char* message);
+
+ // The name of the source file where the test part took place, or
+ // "" if the source file is unknown.
+ std::string file_name_;
+ // The line in the source file where the test part took place, or -1
+ // if the line number is unknown.
+ int line_number_;
+ std::string summary_; // The test failure summary.
+ std::string message_; // The test failure message.
+};
+
+// Prints a TestPartResult object.
+std::ostream& operator<<(std::ostream& os, const TestPartResult& result);
+
+// An array of TestPartResult objects.
+//
+// Don't inherit from TestPartResultArray as its destructor is not
+// virtual.
+class GTEST_API_ TestPartResultArray {
+ public:
+ TestPartResultArray() {}
+
+ // Appends the given TestPartResult to the array.
+ void Append(const TestPartResult& result);
+
+ // Returns the TestPartResult at the given index (0-based).
+ const TestPartResult& GetTestPartResult(int index) const;
+
+ // Returns the number of TestPartResult objects in the array.
+ int size() const;
+
+ private:
+ std::vector<TestPartResult> array_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray);
+};
+
+// This interface knows how to report a test part result.
+class TestPartResultReporterInterface {
+ public:
+ virtual ~TestPartResultReporterInterface() {}
+
+ virtual void ReportTestPartResult(const TestPartResult& result) = 0;
+};
+
+namespace internal {
+
+// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a
+// statement generates new fatal failures. To do so it registers itself as the
+// current test part result reporter. Besides checking if fatal failures were
+// reported, it only delegates the reporting to the former result reporter.
+// The original result reporter is restored in the destructor.
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+class GTEST_API_ HasNewFatalFailureHelper
+ : public TestPartResultReporterInterface {
+ public:
+ HasNewFatalFailureHelper();
+ virtual ~HasNewFatalFailureHelper();
+ virtual void ReportTestPartResult(const TestPartResult& result);
+ bool has_new_fatal_failure() const { return has_new_fatal_failure_; }
+ private:
+ bool has_new_fatal_failure_;
+ TestPartResultReporterInterface* original_reporter_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper);
+};
+
+} // namespace internal
+
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
diff --git a/extern/gtest/include/gtest/gtest-typed-test.h b/extern/gtest/include/gtest/gtest-typed-test.h
new file mode 100644
index 00000000000..fe1e83b274b
--- /dev/null
+++ b/extern/gtest/include/gtest/gtest-typed-test.h
@@ -0,0 +1,259 @@
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
+#define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
+
+// This header implements typed tests and type-parameterized tests.
+
+// Typed (aka type-driven) tests repeat the same test for types in a
+// list. You must know which types you want to test with when writing
+// typed tests. Here's how you do it:
+
+#if 0
+
+// First, define a fixture class template. It should be parameterized
+// by a type. Remember to derive it from testing::Test.
+template <typename T>
+class FooTest : public testing::Test {
+ public:
+ ...
+ typedef std::list<T> List;
+ static T shared_;
+ T value_;
+};
+
+// Next, associate a list of types with the test case, which will be
+// repeated for each type in the list. The typedef is necessary for
+// the macro to parse correctly.
+typedef testing::Types<char, int, unsigned int> MyTypes;
+TYPED_TEST_CASE(FooTest, MyTypes);
+
+// If the type list contains only one type, you can write that type
+// directly without Types<...>:
+// TYPED_TEST_CASE(FooTest, int);
+
+// Then, use TYPED_TEST() instead of TEST_F() to define as many typed
+// tests for this test case as you want.
+TYPED_TEST(FooTest, DoesBlah) {
+ // Inside a test, refer to TypeParam to get the type parameter.
+ // Since we are inside a derived class template, C++ requires use to
+ // visit the members of FooTest via 'this'.
+ TypeParam n = this->value_;
+
+ // To visit static members of the fixture, add the TestFixture::
+ // prefix.
+ n += TestFixture::shared_;
+
+ // To refer to typedefs in the fixture, add the "typename
+ // TestFixture::" prefix.
+ typename TestFixture::List values;
+ values.push_back(n);
+ ...
+}
+
+TYPED_TEST(FooTest, HasPropertyA) { ... }
+
+#endif // 0
+
+// Type-parameterized tests are abstract test patterns parameterized
+// by a type. Compared with typed tests, type-parameterized tests
+// allow you to define the test pattern without knowing what the type
+// parameters are. The defined pattern can be instantiated with
+// different types any number of times, in any number of translation
+// units.
+//
+// If you are designing an interface or concept, you can define a
+// suite of type-parameterized tests to verify properties that any
+// valid implementation of the interface/concept should have. Then,
+// each implementation can easily instantiate the test suite to verify
+// that it conforms to the requirements, without having to write
+// similar tests repeatedly. Here's an example:
+
+#if 0
+
+// First, define a fixture class template. It should be parameterized
+// by a type. Remember to derive it from testing::Test.
+template <typename T>
+class FooTest : public testing::Test {
+ ...
+};
+
+// Next, declare that you will define a type-parameterized test case
+// (the _P suffix is for "parameterized" or "pattern", whichever you
+// prefer):
+TYPED_TEST_CASE_P(FooTest);
+
+// Then, use TYPED_TEST_P() to define as many type-parameterized tests
+// for this type-parameterized test case as you want.
+TYPED_TEST_P(FooTest, DoesBlah) {
+ // Inside a test, refer to TypeParam to get the type parameter.
+ TypeParam n = 0;
+ ...
+}
+
+TYPED_TEST_P(FooTest, HasPropertyA) { ... }
+
+// Now the tricky part: you need to register all test patterns before
+// you can instantiate them. The first argument of the macro is the
+// test case name; the rest are the names of the tests in this test
+// case.
+REGISTER_TYPED_TEST_CASE_P(FooTest,
+ DoesBlah, HasPropertyA);
+
+// Finally, you are free to instantiate the pattern with the types you
+// want. If you put the above code in a header file, you can #include
+// it in multiple C++ source files and instantiate it multiple times.
+//
+// To distinguish different instances of the pattern, the first
+// argument to the INSTANTIATE_* macro is a prefix that will be added
+// to the actual test case name. Remember to pick unique prefixes for
+// different instances.
+typedef testing::Types<char, int, unsigned int> MyTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
+
+// If the type list contains only one type, you can write that type
+// directly without Types<...>:
+// INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int);
+
+#endif // 0
+
+#include "gtest/internal/gtest-port.h"
+#include "gtest/internal/gtest-type-util.h"
+
+// Implements typed tests.
+
+#if GTEST_HAS_TYPED_TEST
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Expands to the name of the typedef for the type parameters of the
+// given test case.
+# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_
+
+// The 'Types' template argument below must have spaces around it
+// since some compilers may choke on '>>' when passing a template
+// instance (e.g. Types<int>)
+# define TYPED_TEST_CASE(CaseName, Types) \
+ typedef ::testing::internal::TypeList< Types >::type \
+ GTEST_TYPE_PARAMS_(CaseName)
+
+# define TYPED_TEST(CaseName, TestName) \
+ template <typename gtest_TypeParam_> \
+ class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \
+ : public CaseName<gtest_TypeParam_> { \
+ private: \
+ typedef CaseName<gtest_TypeParam_> TestFixture; \
+ typedef gtest_TypeParam_ TypeParam; \
+ virtual void TestBody(); \
+ }; \
+ bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \
+ ::testing::internal::TypeParameterizedTest< \
+ CaseName, \
+ ::testing::internal::TemplateSel< \
+ GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \
+ GTEST_TYPE_PARAMS_(CaseName)>::Register(\
+ "", #CaseName, #TestName, 0); \
+ template <typename gtest_TypeParam_> \
+ void GTEST_TEST_CLASS_NAME_(CaseName, TestName)<gtest_TypeParam_>::TestBody()
+
+#endif // GTEST_HAS_TYPED_TEST
+
+// Implements type-parameterized tests.
+
+#if GTEST_HAS_TYPED_TEST_P
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Expands to the namespace name that the type-parameterized tests for
+// the given type-parameterized test case are defined in. The exact
+// name of the namespace is subject to change without notice.
+# define GTEST_CASE_NAMESPACE_(TestCaseName) \
+ gtest_case_##TestCaseName##_
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Expands to the name of the variable used to remember the names of
+// the defined tests in the given test case.
+# define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \
+ gtest_typed_test_case_p_state_##TestCaseName##_
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY.
+//
+// Expands to the name of the variable used to remember the names of
+// the registered tests in the given test case.
+# define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \
+ gtest_registered_test_names_##TestCaseName##_
+
+// The variables defined in the type-parameterized test macros are
+// static as typically these macros are used in a .h file that can be
+// #included in multiple translation units linked together.
+# define TYPED_TEST_CASE_P(CaseName) \
+ static ::testing::internal::TypedTestCasePState \
+ GTEST_TYPED_TEST_CASE_P_STATE_(CaseName)
+
+# define TYPED_TEST_P(CaseName, TestName) \
+ namespace GTEST_CASE_NAMESPACE_(CaseName) { \
+ template <typename gtest_TypeParam_> \
+ class TestName : public CaseName<gtest_TypeParam_> { \
+ private: \
+ typedef CaseName<gtest_TypeParam_> TestFixture; \
+ typedef gtest_TypeParam_ TypeParam; \
+ virtual void TestBody(); \
+ }; \
+ static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \
+ GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\
+ __FILE__, __LINE__, #CaseName, #TestName); \
+ } \
+ template <typename gtest_TypeParam_> \
+ void GTEST_CASE_NAMESPACE_(CaseName)::TestName<gtest_TypeParam_>::TestBody()
+
+# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \
+ namespace GTEST_CASE_NAMESPACE_(CaseName) { \
+ typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \
+ } \
+ static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \
+ GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\
+ __FILE__, __LINE__, #__VA_ARGS__)
+
+// The 'Types' template argument below must have spaces around it
+// since some compilers may choke on '>>' when passing a template
+// instance (e.g. Types<int>)
+# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \
+ bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \
+ ::testing::internal::TypeParameterizedTestCase<CaseName, \
+ GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_, \
+ ::testing::internal::TypeList< Types >::type>::Register(\
+ #Prefix, #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName))
+
+#endif // GTEST_HAS_TYPED_TEST_P
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
diff --git a/extern/gtest/include/gtest/gtest.h b/extern/gtest/include/gtest/gtest.h
new file mode 100644
index 00000000000..6fa0a3925e7
--- /dev/null
+++ b/extern/gtest/include/gtest/gtest.h
@@ -0,0 +1,2291 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file defines the public API for Google Test. It should be
+// included by any test program that uses Google Test.
+//
+// IMPORTANT NOTE: Due to limitation of the C++ language, we have to
+// leave some internal implementation details in this header file.
+// They are clearly marked by comments like this:
+//
+// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+//
+// Such code is NOT meant to be used by a user directly, and is subject
+// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user
+// program!
+//
+// Acknowledgment: Google Test borrowed the idea of automatic test
+// registration from Barthelemy Dagenais' (barthelemy@prologique.com)
+// easyUnit framework.
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_H_
+#define GTEST_INCLUDE_GTEST_GTEST_H_
+
+#include <limits>
+#include <ostream>
+#include <vector>
+
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-string.h"
+#include "gtest/gtest-death-test.h"
+#include "gtest/gtest-message.h"
+#include "gtest/gtest-param-test.h"
+#include "gtest/gtest-printers.h"
+#include "gtest/gtest_prod.h"
+#include "gtest/gtest-test-part.h"
+#include "gtest/gtest-typed-test.h"
+
+// Depending on the platform, different string classes are available.
+// On Linux, in addition to ::std::string, Google also makes use of
+// class ::string, which has the same interface as ::std::string, but
+// has a different implementation.
+//
+// The user can define GTEST_HAS_GLOBAL_STRING to 1 to indicate that
+// ::string is available AND is a distinct type to ::std::string, or
+// define it to 0 to indicate otherwise.
+//
+// If the user's ::std::string and ::string are the same class due to
+// aliasing, he should define GTEST_HAS_GLOBAL_STRING to 0.
+//
+// If the user doesn't define GTEST_HAS_GLOBAL_STRING, it is defined
+// heuristically.
+
+namespace testing {
+
+// Declares the flags.
+
+// This flag temporary enables the disabled tests.
+GTEST_DECLARE_bool_(also_run_disabled_tests);
+
+// This flag brings the debugger on an assertion failure.
+GTEST_DECLARE_bool_(break_on_failure);
+
+// This flag controls whether Google Test catches all test-thrown exceptions
+// and logs them as failures.
+GTEST_DECLARE_bool_(catch_exceptions);
+
+// This flag enables using colors in terminal output. Available values are
+// "yes" to enable colors, "no" (disable colors), or "auto" (the default)
+// to let Google Test decide.
+GTEST_DECLARE_string_(color);
+
+// This flag sets up the filter to select by name using a glob pattern
+// the tests to run. If the filter is not given all tests are executed.
+GTEST_DECLARE_string_(filter);
+
+// This flag causes the Google Test to list tests. None of the tests listed
+// are actually run if the flag is provided.
+GTEST_DECLARE_bool_(list_tests);
+
+// This flag controls whether Google Test emits a detailed XML report to a file
+// in addition to its normal textual output.
+GTEST_DECLARE_string_(output);
+
+// This flags control whether Google Test prints the elapsed time for each
+// test.
+GTEST_DECLARE_bool_(print_time);
+
+// This flag specifies the random number seed.
+GTEST_DECLARE_int32_(random_seed);
+
+// This flag sets how many times the tests are repeated. The default value
+// is 1. If the value is -1 the tests are repeating forever.
+GTEST_DECLARE_int32_(repeat);
+
+// This flag controls whether Google Test includes Google Test internal
+// stack frames in failure stack traces.
+GTEST_DECLARE_bool_(show_internal_stack_frames);
+
+// When this flag is specified, tests' order is randomized on every iteration.
+GTEST_DECLARE_bool_(shuffle);
+
+// This flag specifies the maximum number of stack frames to be
+// printed in a failure message.
+GTEST_DECLARE_int32_(stack_trace_depth);
+
+// When this flag is specified, a failed assertion will throw an
+// exception if exceptions are enabled, or exit the program with a
+// non-zero code otherwise.
+GTEST_DECLARE_bool_(throw_on_failure);
+
+// When this flag is set with a "host:port" string, on supported
+// platforms test results are streamed to the specified port on
+// the specified host machine.
+GTEST_DECLARE_string_(stream_result_to);
+
+// The upper limit for valid stack trace depths.
+const int kMaxStackTraceDepth = 100;
+
+namespace internal {
+
+class AssertHelper;
+class DefaultGlobalTestPartResultReporter;
+class ExecDeathTest;
+class NoExecDeathTest;
+class FinalSuccessChecker;
+class GTestFlagSaver;
+class StreamingListenerTest;
+class TestResultAccessor;
+class TestEventListenersAccessor;
+class TestEventRepeater;
+class UnitTestRecordPropertyTestHelper;
+class WindowsDeathTest;
+class UnitTestImpl* GetUnitTestImpl();
+void ReportFailureInUnknownLocation(TestPartResult::Type result_type,
+ const std::string& message);
+
+} // namespace internal
+
+// The friend relationship of some of these classes is cyclic.
+// If we don't forward declare them the compiler might confuse the classes
+// in friendship clauses with same named classes on the scope.
+class Test;
+class TestCase;
+class TestInfo;
+class UnitTest;
+
+// A class for indicating whether an assertion was successful. When
+// the assertion wasn't successful, the AssertionResult object
+// remembers a non-empty message that describes how it failed.
+//
+// To create an instance of this class, use one of the factory functions
+// (AssertionSuccess() and AssertionFailure()).
+//
+// This class is useful for two purposes:
+// 1. Defining predicate functions to be used with Boolean test assertions
+// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts
+// 2. Defining predicate-format functions to be
+// used with predicate assertions (ASSERT_PRED_FORMAT*, etc).
+//
+// For example, if you define IsEven predicate:
+//
+// testing::AssertionResult IsEven(int n) {
+// if ((n % 2) == 0)
+// return testing::AssertionSuccess();
+// else
+// return testing::AssertionFailure() << n << " is odd";
+// }
+//
+// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5)))
+// will print the message
+//
+// Value of: IsEven(Fib(5))
+// Actual: false (5 is odd)
+// Expected: true
+//
+// instead of a more opaque
+//
+// Value of: IsEven(Fib(5))
+// Actual: false
+// Expected: true
+//
+// in case IsEven is a simple Boolean predicate.
+//
+// If you expect your predicate to be reused and want to support informative
+// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up
+// about half as often as positive ones in our tests), supply messages for
+// both success and failure cases:
+//
+// testing::AssertionResult IsEven(int n) {
+// if ((n % 2) == 0)
+// return testing::AssertionSuccess() << n << " is even";
+// else
+// return testing::AssertionFailure() << n << " is odd";
+// }
+//
+// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print
+//
+// Value of: IsEven(Fib(6))
+// Actual: true (8 is even)
+// Expected: false
+//
+// NB: Predicates that support negative Boolean assertions have reduced
+// performance in positive ones so be careful not to use them in tests
+// that have lots (tens of thousands) of positive Boolean assertions.
+//
+// To use this class with EXPECT_PRED_FORMAT assertions such as:
+//
+// // Verifies that Foo() returns an even number.
+// EXPECT_PRED_FORMAT1(IsEven, Foo());
+//
+// you need to define:
+//
+// testing::AssertionResult IsEven(const char* expr, int n) {
+// if ((n % 2) == 0)
+// return testing::AssertionSuccess();
+// else
+// return testing::AssertionFailure()
+// << "Expected: " << expr << " is even\n Actual: it's " << n;
+// }
+//
+// If Foo() returns 5, you will see the following message:
+//
+// Expected: Foo() is even
+// Actual: it's 5
+//
+class GTEST_API_ AssertionResult {
+ public:
+ // Copy constructor.
+ // Used in EXPECT_TRUE/FALSE(assertion_result).
+ AssertionResult(const AssertionResult& other);
+ // Used in the EXPECT_TRUE/FALSE(bool_expression).
+ explicit AssertionResult(bool success) : success_(success) {}
+
+ // Returns true iff the assertion succeeded.
+ operator bool() const { return success_; } // NOLINT
+
+ // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE.
+ AssertionResult operator!() const;
+
+ // Returns the text streamed into this AssertionResult. Test assertions
+ // use it when they fail (i.e., the predicate's outcome doesn't match the
+ // assertion's expectation). When nothing has been streamed into the
+ // object, returns an empty string.
+ const char* message() const {
+ return message_.get() != NULL ? message_->c_str() : "";
+ }
+ // TODO(vladl@google.com): Remove this after making sure no clients use it.
+ // Deprecated; please use message() instead.
+ const char* failure_message() const { return message(); }
+
+ // Streams a custom failure message into this object.
+ template <typename T> AssertionResult& operator<<(const T& value) {
+ AppendMessage(Message() << value);
+ return *this;
+ }
+
+ // Allows streaming basic output manipulators such as endl or flush into
+ // this object.
+ AssertionResult& operator<<(
+ ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) {
+ AppendMessage(Message() << basic_manipulator);
+ return *this;
+ }
+
+ private:
+ // Appends the contents of message to message_.
+ void AppendMessage(const Message& a_message) {
+ if (message_.get() == NULL)
+ message_.reset(new ::std::string);
+ message_->append(a_message.GetString().c_str());
+ }
+
+ // Stores result of the assertion predicate.
+ bool success_;
+ // Stores the message describing the condition in case the expectation
+ // construct is not satisfied with the predicate's outcome.
+ // Referenced via a pointer to avoid taking too much stack frame space
+ // with test assertions.
+ internal::scoped_ptr< ::std::string> message_;
+
+ GTEST_DISALLOW_ASSIGN_(AssertionResult);
+};
+
+// Makes a successful assertion result.
+GTEST_API_ AssertionResult AssertionSuccess();
+
+// Makes a failed assertion result.
+GTEST_API_ AssertionResult AssertionFailure();
+
+// Makes a failed assertion result with the given failure message.
+// Deprecated; use AssertionFailure() << msg.
+GTEST_API_ AssertionResult AssertionFailure(const Message& msg);
+
+// The abstract class that all tests inherit from.
+//
+// In Google Test, a unit test program contains one or many TestCases, and
+// each TestCase contains one or many Tests.
+//
+// When you define a test using the TEST macro, you don't need to
+// explicitly derive from Test - the TEST macro automatically does
+// this for you.
+//
+// The only time you derive from Test is when defining a test fixture
+// to be used a TEST_F. For example:
+//
+// class FooTest : public testing::Test {
+// protected:
+// virtual void SetUp() { ... }
+// virtual void TearDown() { ... }
+// ...
+// };
+//
+// TEST_F(FooTest, Bar) { ... }
+// TEST_F(FooTest, Baz) { ... }
+//
+// Test is not copyable.
+class GTEST_API_ Test {
+ public:
+ friend class TestInfo;
+
+ // Defines types for pointers to functions that set up and tear down
+ // a test case.
+ typedef internal::SetUpTestCaseFunc SetUpTestCaseFunc;
+ typedef internal::TearDownTestCaseFunc TearDownTestCaseFunc;
+
+ // The d'tor is virtual as we intend to inherit from Test.
+ virtual ~Test();
+
+ // Sets up the stuff shared by all tests in this test case.
+ //
+ // Google Test will call Foo::SetUpTestCase() before running the first
+ // test in test case Foo. Hence a sub-class can define its own
+ // SetUpTestCase() method to shadow the one defined in the super
+ // class.
+ static void SetUpTestCase() {}
+
+ // Tears down the stuff shared by all tests in this test case.
+ //
+ // Google Test will call Foo::TearDownTestCase() after running the last
+ // test in test case Foo. Hence a sub-class can define its own
+ // TearDownTestCase() method to shadow the one defined in the super
+ // class.
+ static void TearDownTestCase() {}
+
+ // Returns true iff the current test has a fatal failure.
+ static bool HasFatalFailure();
+
+ // Returns true iff the current test has a non-fatal failure.
+ static bool HasNonfatalFailure();
+
+ // Returns true iff the current test has a (either fatal or
+ // non-fatal) failure.
+ static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); }
+
+ // Logs a property for the current test, test case, or for the entire
+ // invocation of the test program when used outside of the context of a
+ // test case. Only the last value for a given key is remembered. These
+ // are public static so they can be called from utility functions that are
+ // not members of the test fixture. Calls to RecordProperty made during
+ // lifespan of the test (from the moment its constructor starts to the
+ // moment its destructor finishes) will be output in XML as attributes of
+ // the <testcase> element. Properties recorded from fixture's
+ // SetUpTestCase or TearDownTestCase are logged as attributes of the
+ // corresponding <testsuite> element. Calls to RecordProperty made in the
+ // global context (before or after invocation of RUN_ALL_TESTS and from
+ // SetUp/TearDown method of Environment objects registered with Google
+ // Test) will be output as attributes of the <testsuites> element.
+ static void RecordProperty(const std::string& key, const std::string& value);
+ static void RecordProperty(const std::string& key, int value);
+
+ protected:
+ // Creates a Test object.
+ Test();
+
+ // Sets up the test fixture.
+ virtual void SetUp();
+
+ // Tears down the test fixture.
+ virtual void TearDown();
+
+ private:
+ // Returns true iff the current test has the same fixture class as
+ // the first test in the current test case.
+ static bool HasSameFixtureClass();
+
+ // Runs the test after the test fixture has been set up.
+ //
+ // A sub-class must implement this to define the test logic.
+ //
+ // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM.
+ // Instead, use the TEST or TEST_F macro.
+ virtual void TestBody() = 0;
+
+ // Sets up, executes, and tears down the test.
+ void Run();
+
+ // Deletes self. We deliberately pick an unusual name for this
+ // internal method to avoid clashing with names used in user TESTs.
+ void DeleteSelf_() { delete this; }
+
+ // Uses a GTestFlagSaver to save and restore all Google Test flags.
+ const internal::GTestFlagSaver* const gtest_flag_saver_;
+
+ // Often a user mis-spells SetUp() as Setup() and spends a long time
+ // wondering why it is never called by Google Test. The declaration of
+ // the following method is solely for catching such an error at
+ // compile time:
+ //
+ // - The return type is deliberately chosen to be not void, so it
+ // will be a conflict if a user declares void Setup() in his test
+ // fixture.
+ //
+ // - This method is private, so it will be another compiler error
+ // if a user calls it from his test fixture.
+ //
+ // DO NOT OVERRIDE THIS FUNCTION.
+ //
+ // If you see an error about overriding the following function or
+ // about it being private, you have mis-spelled SetUp() as Setup().
+ struct Setup_should_be_spelled_SetUp {};
+ virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; }
+
+ // We disallow copying Tests.
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(Test);
+};
+
+typedef internal::TimeInMillis TimeInMillis;
+
+// A copyable object representing a user specified test property which can be
+// output as a key/value string pair.
+//
+// Don't inherit from TestProperty as its destructor is not virtual.
+class TestProperty {
+ public:
+ // C'tor. TestProperty does NOT have a default constructor.
+ // Always use this constructor (with parameters) to create a
+ // TestProperty object.
+ TestProperty(const std::string& a_key, const std::string& a_value) :
+ key_(a_key), value_(a_value) {
+ }
+
+ // Gets the user supplied key.
+ const char* key() const {
+ return key_.c_str();
+ }
+
+ // Gets the user supplied value.
+ const char* value() const {
+ return value_.c_str();
+ }
+
+ // Sets a new value, overriding the one supplied in the constructor.
+ void SetValue(const std::string& new_value) {
+ value_ = new_value;
+ }
+
+ private:
+ // The key supplied by the user.
+ std::string key_;
+ // The value supplied by the user.
+ std::string value_;
+};
+
+// The result of a single Test. This includes a list of
+// TestPartResults, a list of TestProperties, a count of how many
+// death tests there are in the Test, and how much time it took to run
+// the Test.
+//
+// TestResult is not copyable.
+class GTEST_API_ TestResult {
+ public:
+ // Creates an empty TestResult.
+ TestResult();
+
+ // D'tor. Do not inherit from TestResult.
+ ~TestResult();
+
+ // Gets the number of all test parts. This is the sum of the number
+ // of successful test parts and the number of failed test parts.
+ int total_part_count() const;
+
+ // Returns the number of the test properties.
+ int test_property_count() const;
+
+ // Returns true iff the test passed (i.e. no test part failed).
+ bool Passed() const { return !Failed(); }
+
+ // Returns true iff the test failed.
+ bool Failed() const;
+
+ // Returns true iff the test fatally failed.
+ bool HasFatalFailure() const;
+
+ // Returns true iff the test has a non-fatal failure.
+ bool HasNonfatalFailure() const;
+
+ // Returns the elapsed time, in milliseconds.
+ TimeInMillis elapsed_time() const { return elapsed_time_; }
+
+ // Returns the i-th test part result among all the results. i can range
+ // from 0 to test_property_count() - 1. If i is not in that range, aborts
+ // the program.
+ const TestPartResult& GetTestPartResult(int i) const;
+
+ // Returns the i-th test property. i can range from 0 to
+ // test_property_count() - 1. If i is not in that range, aborts the
+ // program.
+ const TestProperty& GetTestProperty(int i) const;
+
+ private:
+ friend class TestInfo;
+ friend class TestCase;
+ friend class UnitTest;
+ friend class internal::DefaultGlobalTestPartResultReporter;
+ friend class internal::ExecDeathTest;
+ friend class internal::TestResultAccessor;
+ friend class internal::UnitTestImpl;
+ friend class internal::WindowsDeathTest;
+
+ // Gets the vector of TestPartResults.
+ const std::vector<TestPartResult>& test_part_results() const {
+ return test_part_results_;
+ }
+
+ // Gets the vector of TestProperties.
+ const std::vector<TestProperty>& test_properties() const {
+ return test_properties_;
+ }
+
+ // Sets the elapsed time.
+ void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; }
+
+ // Adds a test property to the list. The property is validated and may add
+ // a non-fatal failure if invalid (e.g., if it conflicts with reserved
+ // key names). If a property is already recorded for the same key, the
+ // value will be updated, rather than storing multiple values for the same
+ // key. xml_element specifies the element for which the property is being
+ // recorded and is used for validation.
+ void RecordProperty(const std::string& xml_element,
+ const TestProperty& test_property);
+
+ // Adds a failure if the key is a reserved attribute of Google Test
+ // testcase tags. Returns true if the property is valid.
+ // TODO(russr): Validate attribute names are legal and human readable.
+ static bool ValidateTestProperty(const std::string& xml_element,
+ const TestProperty& test_property);
+
+ // Adds a test part result to the list.
+ void AddTestPartResult(const TestPartResult& test_part_result);
+
+ // Returns the death test count.
+ int death_test_count() const { return death_test_count_; }
+
+ // Increments the death test count, returning the new count.
+ int increment_death_test_count() { return ++death_test_count_; }
+
+ // Clears the test part results.
+ void ClearTestPartResults();
+
+ // Clears the object.
+ void Clear();
+
+ // Protects mutable state of the property vector and of owned
+ // properties, whose values may be updated.
+ internal::Mutex test_properites_mutex_;
+
+ // The vector of TestPartResults
+ std::vector<TestPartResult> test_part_results_;
+ // The vector of TestProperties
+ std::vector<TestProperty> test_properties_;
+ // Running count of death tests.
+ int death_test_count_;
+ // The elapsed time, in milliseconds.
+ TimeInMillis elapsed_time_;
+
+ // We disallow copying TestResult.
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestResult);
+}; // class TestResult
+
+// A TestInfo object stores the following information about a test:
+//
+// Test case name
+// Test name
+// Whether the test should be run
+// A function pointer that creates the test object when invoked
+// Test result
+//
+// The constructor of TestInfo registers itself with the UnitTest
+// singleton such that the RUN_ALL_TESTS() macro knows which tests to
+// run.
+class GTEST_API_ TestInfo {
+ public:
+ // Destructs a TestInfo object. This function is not virtual, so
+ // don't inherit from TestInfo.
+ ~TestInfo();
+
+ // Returns the test case name.
+ const char* test_case_name() const { return test_case_name_.c_str(); }
+
+ // Returns the test name.
+ const char* name() const { return name_.c_str(); }
+
+ // Returns the name of the parameter type, or NULL if this is not a typed
+ // or a type-parameterized test.
+ const char* type_param() const {
+ if (type_param_.get() != NULL)
+ return type_param_->c_str();
+ return NULL;
+ }
+
+ // Returns the text representation of the value parameter, or NULL if this
+ // is not a value-parameterized test.
+ const char* value_param() const {
+ if (value_param_.get() != NULL)
+ return value_param_->c_str();
+ return NULL;
+ }
+
+ // Returns true if this test should run, that is if the test is not
+ // disabled (or it is disabled but the also_run_disabled_tests flag has
+ // been specified) and its full name matches the user-specified filter.
+ //
+ // Google Test allows the user to filter the tests by their full names.
+ // The full name of a test Bar in test case Foo is defined as
+ // "Foo.Bar". Only the tests that match the filter will run.
+ //
+ // A filter is a colon-separated list of glob (not regex) patterns,
+ // optionally followed by a '-' and a colon-separated list of
+ // negative patterns (tests to exclude). A test is run if it
+ // matches one of the positive patterns and does not match any of
+ // the negative patterns.
+ //
+ // For example, *A*:Foo.* is a filter that matches any string that
+ // contains the character 'A' or starts with "Foo.".
+ bool should_run() const { return should_run_; }
+
+ // Returns true iff this test will appear in the XML report.
+ bool is_reportable() const {
+ // For now, the XML report includes all tests matching the filter.
+ // In the future, we may trim tests that are excluded because of
+ // sharding.
+ return matches_filter_;
+ }
+
+ // Returns the result of the test.
+ const TestResult* result() const { return &result_; }
+
+ private:
+#if GTEST_HAS_DEATH_TEST
+ friend class internal::DefaultDeathTestFactory;
+#endif // GTEST_HAS_DEATH_TEST
+ friend class Test;
+ friend class TestCase;
+ friend class internal::UnitTestImpl;
+ friend class internal::StreamingListenerTest;
+ friend TestInfo* internal::MakeAndRegisterTestInfo(
+ const char* test_case_name,
+ const char* name,
+ const char* type_param,
+ const char* value_param,
+ internal::TypeId fixture_class_id,
+ Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc,
+ internal::TestFactoryBase* factory);
+
+ // Constructs a TestInfo object. The newly constructed instance assumes
+ // ownership of the factory object.
+ TestInfo(const std::string& test_case_name,
+ const std::string& name,
+ const char* a_type_param, // NULL if not a type-parameterized test
+ const char* a_value_param, // NULL if not a value-parameterized test
+ internal::TypeId fixture_class_id,
+ internal::TestFactoryBase* factory);
+
+ // Increments the number of death tests encountered in this test so
+ // far.
+ int increment_death_test_count() {
+ return result_.increment_death_test_count();
+ }
+
+ // Creates the test object, runs it, records its result, and then
+ // deletes it.
+ void Run();
+
+ static void ClearTestResult(TestInfo* test_info) {
+ test_info->result_.Clear();
+ }
+
+ // These fields are immutable properties of the test.
+ const std::string test_case_name_; // Test case name
+ const std::string name_; // Test name
+ // Name of the parameter type, or NULL if this is not a typed or a
+ // type-parameterized test.
+ const internal::scoped_ptr<const ::std::string> type_param_;
+ // Text representation of the value parameter, or NULL if this is not a
+ // value-parameterized test.
+ const internal::scoped_ptr<const ::std::string> value_param_;
+ const internal::TypeId fixture_class_id_; // ID of the test fixture class
+ bool should_run_; // True iff this test should run
+ bool is_disabled_; // True iff this test is disabled
+ bool matches_filter_; // True if this test matches the
+ // user-specified filter.
+ internal::TestFactoryBase* const factory_; // The factory that creates
+ // the test object
+
+ // This field is mutable and needs to be reset before running the
+ // test for the second time.
+ TestResult result_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo);
+};
+
+// A test case, which consists of a vector of TestInfos.
+//
+// TestCase is not copyable.
+class GTEST_API_ TestCase {
+ public:
+ // Creates a TestCase with the given name.
+ //
+ // TestCase does NOT have a default constructor. Always use this
+ // constructor to create a TestCase object.
+ //
+ // Arguments:
+ //
+ // name: name of the test case
+ // a_type_param: the name of the test's type parameter, or NULL if
+ // this is not a type-parameterized test.
+ // set_up_tc: pointer to the function that sets up the test case
+ // tear_down_tc: pointer to the function that tears down the test case
+ TestCase(const char* name, const char* a_type_param,
+ Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc);
+
+ // Destructor of TestCase.
+ virtual ~TestCase();
+
+ // Gets the name of the TestCase.
+ const char* name() const { return name_.c_str(); }
+
+ // Returns the name of the parameter type, or NULL if this is not a
+ // type-parameterized test case.
+ const char* type_param() const {
+ if (type_param_.get() != NULL)
+ return type_param_->c_str();
+ return NULL;
+ }
+
+ // Returns true if any test in this test case should run.
+ bool should_run() const { return should_run_; }
+
+ // Gets the number of successful tests in this test case.
+ int successful_test_count() const;
+
+ // Gets the number of failed tests in this test case.
+ int failed_test_count() const;
+
+ // Gets the number of disabled tests that will be reported in the XML report.
+ int reportable_disabled_test_count() const;
+
+ // Gets the number of disabled tests in this test case.
+ int disabled_test_count() const;
+
+ // Gets the number of tests to be printed in the XML report.
+ int reportable_test_count() const;
+
+ // Get the number of tests in this test case that should run.
+ int test_to_run_count() const;
+
+ // Gets the number of all tests in this test case.
+ int total_test_count() const;
+
+ // Returns true iff the test case passed.
+ bool Passed() const { return !Failed(); }
+
+ // Returns true iff the test case failed.
+ bool Failed() const { return failed_test_count() > 0; }
+
+ // Returns the elapsed time, in milliseconds.
+ TimeInMillis elapsed_time() const { return elapsed_time_; }
+
+ // Returns the i-th test among all the tests. i can range from 0 to
+ // total_test_count() - 1. If i is not in that range, returns NULL.
+ const TestInfo* GetTestInfo(int i) const;
+
+ // Returns the TestResult that holds test properties recorded during
+ // execution of SetUpTestCase and TearDownTestCase.
+ const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; }
+
+ private:
+ friend class Test;
+ friend class internal::UnitTestImpl;
+
+ // Gets the (mutable) vector of TestInfos in this TestCase.
+ std::vector<TestInfo*>& test_info_list() { return test_info_list_; }
+
+ // Gets the (immutable) vector of TestInfos in this TestCase.
+ const std::vector<TestInfo*>& test_info_list() const {
+ return test_info_list_;
+ }
+
+ // Returns the i-th test among all the tests. i can range from 0 to
+ // total_test_count() - 1. If i is not in that range, returns NULL.
+ TestInfo* GetMutableTestInfo(int i);
+
+ // Sets the should_run member.
+ void set_should_run(bool should) { should_run_ = should; }
+
+ // Adds a TestInfo to this test case. Will delete the TestInfo upon
+ // destruction of the TestCase object.
+ void AddTestInfo(TestInfo * test_info);
+
+ // Clears the results of all tests in this test case.
+ void ClearResult();
+
+ // Clears the results of all tests in the given test case.
+ static void ClearTestCaseResult(TestCase* test_case) {
+ test_case->ClearResult();
+ }
+
+ // Runs every test in this TestCase.
+ void Run();
+
+ // Runs SetUpTestCase() for this TestCase. This wrapper is needed
+ // for catching exceptions thrown from SetUpTestCase().
+ void RunSetUpTestCase() { (*set_up_tc_)(); }
+
+ // Runs TearDownTestCase() for this TestCase. This wrapper is
+ // needed for catching exceptions thrown from TearDownTestCase().
+ void RunTearDownTestCase() { (*tear_down_tc_)(); }
+
+ // Returns true iff test passed.
+ static bool TestPassed(const TestInfo* test_info) {
+ return test_info->should_run() && test_info->result()->Passed();
+ }
+
+ // Returns true iff test failed.
+ static bool TestFailed(const TestInfo* test_info) {
+ return test_info->should_run() && test_info->result()->Failed();
+ }
+
+ // Returns true iff the test is disabled and will be reported in the XML
+ // report.
+ static bool TestReportableDisabled(const TestInfo* test_info) {
+ return test_info->is_reportable() && test_info->is_disabled_;
+ }
+
+ // Returns true iff test is disabled.
+ static bool TestDisabled(const TestInfo* test_info) {
+ return test_info->is_disabled_;
+ }
+
+ // Returns true iff this test will appear in the XML report.
+ static bool TestReportable(const TestInfo* test_info) {
+ return test_info->is_reportable();
+ }
+
+ // Returns true if the given test should run.
+ static bool ShouldRunTest(const TestInfo* test_info) {
+ return test_info->should_run();
+ }
+
+ // Shuffles the tests in this test case.
+ void ShuffleTests(internal::Random* random);
+
+ // Restores the test order to before the first shuffle.
+ void UnshuffleTests();
+
+ // Name of the test case.
+ std::string name_;
+ // Name of the parameter type, or NULL if this is not a typed or a
+ // type-parameterized test.
+ const internal::scoped_ptr<const ::std::string> type_param_;
+ // The vector of TestInfos in their original order. It owns the
+ // elements in the vector.
+ std::vector<TestInfo*> test_info_list_;
+ // Provides a level of indirection for the test list to allow easy
+ // shuffling and restoring the test order. The i-th element in this
+ // vector is the index of the i-th test in the shuffled test list.
+ std::vector<int> test_indices_;
+ // Pointer to the function that sets up the test case.
+ Test::SetUpTestCaseFunc set_up_tc_;
+ // Pointer to the function that tears down the test case.
+ Test::TearDownTestCaseFunc tear_down_tc_;
+ // True iff any test in this test case should run.
+ bool should_run_;
+ // Elapsed time, in milliseconds.
+ TimeInMillis elapsed_time_;
+ // Holds test properties recorded during execution of SetUpTestCase and
+ // TearDownTestCase.
+ TestResult ad_hoc_test_result_;
+
+ // We disallow copying TestCases.
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase);
+};
+
+// An Environment object is capable of setting up and tearing down an
+// environment. The user should subclass this to define his own
+// environment(s).
+//
+// An Environment object does the set-up and tear-down in virtual
+// methods SetUp() and TearDown() instead of the constructor and the
+// destructor, as:
+//
+// 1. You cannot safely throw from a destructor. This is a problem
+// as in some cases Google Test is used where exceptions are enabled, and
+// we may want to implement ASSERT_* using exceptions where they are
+// available.
+// 2. You cannot use ASSERT_* directly in a constructor or
+// destructor.
+class Environment {
+ public:
+ // The d'tor is virtual as we need to subclass Environment.
+ virtual ~Environment() {}
+
+ // Override this to define how to set up the environment.
+ virtual void SetUp() {}
+
+ // Override this to define how to tear down the environment.
+ virtual void TearDown() {}
+ private:
+ // If you see an error about overriding the following function or
+ // about it being private, you have mis-spelled SetUp() as Setup().
+ struct Setup_should_be_spelled_SetUp {};
+ virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; }
+};
+
+// The interface for tracing execution of tests. The methods are organized in
+// the order the corresponding events are fired.
+class TestEventListener {
+ public:
+ virtual ~TestEventListener() {}
+
+ // Fired before any test activity starts.
+ virtual void OnTestProgramStart(const UnitTest& unit_test) = 0;
+
+ // Fired before each iteration of tests starts. There may be more than
+ // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration
+ // index, starting from 0.
+ virtual void OnTestIterationStart(const UnitTest& unit_test,
+ int iteration) = 0;
+
+ // Fired before environment set-up for each iteration of tests starts.
+ virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0;
+
+ // Fired after environment set-up for each iteration of tests ends.
+ virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0;
+
+ // Fired before the test case starts.
+ virtual void OnTestCaseStart(const TestCase& test_case) = 0;
+
+ // Fired before the test starts.
+ virtual void OnTestStart(const TestInfo& test_info) = 0;
+
+ // Fired after a failed assertion or a SUCCEED() invocation.
+ virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0;
+
+ // Fired after the test ends.
+ virtual void OnTestEnd(const TestInfo& test_info) = 0;
+
+ // Fired after the test case ends.
+ virtual void OnTestCaseEnd(const TestCase& test_case) = 0;
+
+ // Fired before environment tear-down for each iteration of tests starts.
+ virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0;
+
+ // Fired after environment tear-down for each iteration of tests ends.
+ virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0;
+
+ // Fired after each iteration of tests finishes.
+ virtual void OnTestIterationEnd(const UnitTest& unit_test,
+ int iteration) = 0;
+
+ // Fired after all test activities have ended.
+ virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0;
+};
+
+// The convenience class for users who need to override just one or two
+// methods and are not concerned that a possible change to a signature of
+// the methods they override will not be caught during the build. For
+// comments about each method please see the definition of TestEventListener
+// above.
+class EmptyTestEventListener : public TestEventListener {
+ public:
+ virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {}
+ virtual void OnTestIterationStart(const UnitTest& /*unit_test*/,
+ int /*iteration*/) {}
+ virtual void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) {}
+ virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {}
+ virtual void OnTestCaseStart(const TestCase& /*test_case*/) {}
+ virtual void OnTestStart(const TestInfo& /*test_info*/) {}
+ virtual void OnTestPartResult(const TestPartResult& /*test_part_result*/) {}
+ virtual void OnTestEnd(const TestInfo& /*test_info*/) {}
+ virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {}
+ virtual void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) {}
+ virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {}
+ virtual void OnTestIterationEnd(const UnitTest& /*unit_test*/,
+ int /*iteration*/) {}
+ virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {}
+};
+
+// TestEventListeners lets users add listeners to track events in Google Test.
+class GTEST_API_ TestEventListeners {
+ public:
+ TestEventListeners();
+ ~TestEventListeners();
+
+ // Appends an event listener to the end of the list. Google Test assumes
+ // the ownership of the listener (i.e. it will delete the listener when
+ // the test program finishes).
+ void Append(TestEventListener* listener);
+
+ // Removes the given event listener from the list and returns it. It then
+ // becomes the caller's responsibility to delete the listener. Returns
+ // NULL if the listener is not found in the list.
+ TestEventListener* Release(TestEventListener* listener);
+
+ // Returns the standard listener responsible for the default console
+ // output. Can be removed from the listeners list to shut down default
+ // console output. Note that removing this object from the listener list
+ // with Release transfers its ownership to the caller and makes this
+ // function return NULL the next time.
+ TestEventListener* default_result_printer() const {
+ return default_result_printer_;
+ }
+
+ // Returns the standard listener responsible for the default XML output
+ // controlled by the --gtest_output=xml flag. Can be removed from the
+ // listeners list by users who want to shut down the default XML output
+ // controlled by this flag and substitute it with custom one. Note that
+ // removing this object from the listener list with Release transfers its
+ // ownership to the caller and makes this function return NULL the next
+ // time.
+ TestEventListener* default_xml_generator() const {
+ return default_xml_generator_;
+ }
+
+ private:
+ friend class TestCase;
+ friend class TestInfo;
+ friend class internal::DefaultGlobalTestPartResultReporter;
+ friend class internal::NoExecDeathTest;
+ friend class internal::TestEventListenersAccessor;
+ friend class internal::UnitTestImpl;
+
+ // Returns repeater that broadcasts the TestEventListener events to all
+ // subscribers.
+ TestEventListener* repeater();
+
+ // Sets the default_result_printer attribute to the provided listener.
+ // The listener is also added to the listener list and previous
+ // default_result_printer is removed from it and deleted. The listener can
+ // also be NULL in which case it will not be added to the list. Does
+ // nothing if the previous and the current listener objects are the same.
+ void SetDefaultResultPrinter(TestEventListener* listener);
+
+ // Sets the default_xml_generator attribute to the provided listener. The
+ // listener is also added to the listener list and previous
+ // default_xml_generator is removed from it and deleted. The listener can
+ // also be NULL in which case it will not be added to the list. Does
+ // nothing if the previous and the current listener objects are the same.
+ void SetDefaultXmlGenerator(TestEventListener* listener);
+
+ // Controls whether events will be forwarded by the repeater to the
+ // listeners in the list.
+ bool EventForwardingEnabled() const;
+ void SuppressEventForwarding();
+
+ // The actual list of listeners.
+ internal::TestEventRepeater* repeater_;
+ // Listener responsible for the standard result output.
+ TestEventListener* default_result_printer_;
+ // Listener responsible for the creation of the XML output file.
+ TestEventListener* default_xml_generator_;
+
+ // We disallow copying TestEventListeners.
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventListeners);
+};
+
+// A UnitTest consists of a vector of TestCases.
+//
+// This is a singleton class. The only instance of UnitTest is
+// created when UnitTest::GetInstance() is first called. This
+// instance is never deleted.
+//
+// UnitTest is not copyable.
+//
+// This class is thread-safe as long as the methods are called
+// according to their specification.
+class GTEST_API_ UnitTest {
+ public:
+ // Gets the singleton UnitTest object. The first time this method
+ // is called, a UnitTest object is constructed and returned.
+ // Consecutive calls will return the same object.
+ static UnitTest* GetInstance();
+
+ // Runs all tests in this UnitTest object and prints the result.
+ // Returns 0 if successful, or 1 otherwise.
+ //
+ // This method can only be called from the main thread.
+ //
+ // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+ int Run() GTEST_MUST_USE_RESULT_;
+
+ // Returns the working directory when the first TEST() or TEST_F()
+ // was executed. The UnitTest object owns the string.
+ const char* original_working_dir() const;
+
+ // Returns the TestCase object for the test that's currently running,
+ // or NULL if no test is running.
+ const TestCase* current_test_case() const
+ GTEST_LOCK_EXCLUDED_(mutex_);
+
+ // Returns the TestInfo object for the test that's currently running,
+ // or NULL if no test is running.
+ const TestInfo* current_test_info() const
+ GTEST_LOCK_EXCLUDED_(mutex_);
+
+ // Returns the random seed used at the start of the current test run.
+ int random_seed() const;
+
+#if GTEST_HAS_PARAM_TEST
+ // Returns the ParameterizedTestCaseRegistry object used to keep track of
+ // value-parameterized tests and instantiate and register them.
+ //
+ // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+ internal::ParameterizedTestCaseRegistry& parameterized_test_registry()
+ GTEST_LOCK_EXCLUDED_(mutex_);
+#endif // GTEST_HAS_PARAM_TEST
+
+ // Gets the number of successful test cases.
+ int successful_test_case_count() const;
+
+ // Gets the number of failed test cases.
+ int failed_test_case_count() const;
+
+ // Gets the number of all test cases.
+ int total_test_case_count() const;
+
+ // Gets the number of all test cases that contain at least one test
+ // that should run.
+ int test_case_to_run_count() const;
+
+ // Gets the number of successful tests.
+ int successful_test_count() const;
+
+ // Gets the number of failed tests.
+ int failed_test_count() const;
+
+ // Gets the number of disabled tests that will be reported in the XML report.
+ int reportable_disabled_test_count() const;
+
+ // Gets the number of disabled tests.
+ int disabled_test_count() const;
+
+ // Gets the number of tests to be printed in the XML report.
+ int reportable_test_count() const;
+
+ // Gets the number of all tests.
+ int total_test_count() const;
+
+ // Gets the number of tests that should run.
+ int test_to_run_count() const;
+
+ // Gets the time of the test program start, in ms from the start of the
+ // UNIX epoch.
+ TimeInMillis start_timestamp() const;
+
+ // Gets the elapsed time, in milliseconds.
+ TimeInMillis elapsed_time() const;
+
+ // Returns true iff the unit test passed (i.e. all test cases passed).
+ bool Passed() const;
+
+ // Returns true iff the unit test failed (i.e. some test case failed
+ // or something outside of all tests failed).
+ bool Failed() const;
+
+ // Gets the i-th test case among all the test cases. i can range from 0 to
+ // total_test_case_count() - 1. If i is not in that range, returns NULL.
+ const TestCase* GetTestCase(int i) const;
+
+ // Returns the TestResult containing information on test failures and
+ // properties logged outside of individual test cases.
+ const TestResult& ad_hoc_test_result() const;
+
+ // Returns the list of event listeners that can be used to track events
+ // inside Google Test.
+ TestEventListeners& listeners();
+
+ private:
+ // Registers and returns a global test environment. When a test
+ // program is run, all global test environments will be set-up in
+ // the order they were registered. After all tests in the program
+ // have finished, all global test environments will be torn-down in
+ // the *reverse* order they were registered.
+ //
+ // The UnitTest object takes ownership of the given environment.
+ //
+ // This method can only be called from the main thread.
+ Environment* AddEnvironment(Environment* env);
+
+ // Adds a TestPartResult to the current TestResult object. All
+ // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc)
+ // eventually call this to report their results. The user code
+ // should use the assertion macros instead of calling this directly.
+ void AddTestPartResult(TestPartResult::Type result_type,
+ const char* file_name,
+ int line_number,
+ const std::string& message,
+ const std::string& os_stack_trace)
+ GTEST_LOCK_EXCLUDED_(mutex_);
+
+ // Adds a TestProperty to the current TestResult object when invoked from
+ // inside a test, to current TestCase's ad_hoc_test_result_ when invoked
+ // from SetUpTestCase or TearDownTestCase, or to the global property set
+ // when invoked elsewhere. If the result already contains a property with
+ // the same key, the value will be updated.
+ void RecordProperty(const std::string& key, const std::string& value);
+
+ // Gets the i-th test case among all the test cases. i can range from 0 to
+ // total_test_case_count() - 1. If i is not in that range, returns NULL.
+ TestCase* GetMutableTestCase(int i);
+
+ // Accessors for the implementation object.
+ internal::UnitTestImpl* impl() { return impl_; }
+ const internal::UnitTestImpl* impl() const { return impl_; }
+
+ // These classes and funcions are friends as they need to access private
+ // members of UnitTest.
+ friend class Test;
+ friend class internal::AssertHelper;
+ friend class internal::ScopedTrace;
+ friend class internal::StreamingListenerTest;
+ friend class internal::UnitTestRecordPropertyTestHelper;
+ friend Environment* AddGlobalTestEnvironment(Environment* env);
+ friend internal::UnitTestImpl* internal::GetUnitTestImpl();
+ friend void internal::ReportFailureInUnknownLocation(
+ TestPartResult::Type result_type,
+ const std::string& message);
+
+ // Creates an empty UnitTest.
+ UnitTest();
+
+ // D'tor
+ virtual ~UnitTest();
+
+ // Pushes a trace defined by SCOPED_TRACE() on to the per-thread
+ // Google Test trace stack.
+ void PushGTestTrace(const internal::TraceInfo& trace)
+ GTEST_LOCK_EXCLUDED_(mutex_);
+
+ // Pops a trace from the per-thread Google Test trace stack.
+ void PopGTestTrace()
+ GTEST_LOCK_EXCLUDED_(mutex_);
+
+ // Protects mutable state in *impl_. This is mutable as some const
+ // methods need to lock it too.
+ mutable internal::Mutex mutex_;
+
+ // Opaque implementation object. This field is never changed once
+ // the object is constructed. We don't mark it as const here, as
+ // doing so will cause a warning in the constructor of UnitTest.
+ // Mutable state in *impl_ is protected by mutex_.
+ internal::UnitTestImpl* impl_;
+
+ // We disallow copying UnitTest.
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTest);
+};
+
+// A convenient wrapper for adding an environment for the test
+// program.
+//
+// You should call this before RUN_ALL_TESTS() is called, probably in
+// main(). If you use gtest_main, you need to call this before main()
+// starts for it to take effect. For example, you can define a global
+// variable like this:
+//
+// testing::Environment* const foo_env =
+// testing::AddGlobalTestEnvironment(new FooEnvironment);
+//
+// However, we strongly recommend you to write your own main() and
+// call AddGlobalTestEnvironment() there, as relying on initialization
+// of global variables makes the code harder to read and may cause
+// problems when you register multiple environments from different
+// translation units and the environments have dependencies among them
+// (remember that the compiler doesn't guarantee the order in which
+// global variables from different translation units are initialized).
+inline Environment* AddGlobalTestEnvironment(Environment* env) {
+ return UnitTest::GetInstance()->AddEnvironment(env);
+}
+
+// Initializes Google Test. This must be called before calling
+// RUN_ALL_TESTS(). In particular, it parses a command line for the
+// flags that Google Test recognizes. Whenever a Google Test flag is
+// seen, it is removed from argv, and *argc is decremented.
+//
+// No value is returned. Instead, the Google Test flag variables are
+// updated.
+//
+// Calling the function for the second time has no user-visible effect.
+GTEST_API_ void InitGoogleTest(int* argc, char** argv);
+
+// This overloaded version can be used in Windows programs compiled in
+// UNICODE mode.
+GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv);
+
+namespace internal {
+
+// FormatForComparison<ToPrint, OtherOperand>::Format(value) formats a
+// value of type ToPrint that is an operand of a comparison assertion
+// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in
+// the comparison, and is used to help determine the best way to
+// format the value. In particular, when the value is a C string
+// (char pointer) and the other operand is an STL string object, we
+// want to format the C string as a string, since we know it is
+// compared by value with the string object. If the value is a char
+// pointer but the other operand is not an STL string object, we don't
+// know whether the pointer is supposed to point to a NUL-terminated
+// string, and thus want to print it as a pointer to be safe.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+
+// The default case.
+template <typename ToPrint, typename OtherOperand>
+class FormatForComparison {
+ public:
+ static ::std::string Format(const ToPrint& value) {
+ return ::testing::PrintToString(value);
+ }
+};
+
+// Array.
+template <typename ToPrint, size_t N, typename OtherOperand>
+class FormatForComparison<ToPrint[N], OtherOperand> {
+ public:
+ static ::std::string Format(const ToPrint* value) {
+ return FormatForComparison<const ToPrint*, OtherOperand>::Format(value);
+ }
+};
+
+// By default, print C string as pointers to be safe, as we don't know
+// whether they actually point to a NUL-terminated string.
+
+#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \
+ template <typename OtherOperand> \
+ class FormatForComparison<CharType*, OtherOperand> { \
+ public: \
+ static ::std::string Format(CharType* value) { \
+ return ::testing::PrintToString(static_cast<const void*>(value)); \
+ } \
+ }
+
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t);
+
+#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_
+
+// If a C string is compared with an STL string object, we know it's meant
+// to point to a NUL-terminated string, and thus can print it as a string.
+
+#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \
+ template <> \
+ class FormatForComparison<CharType*, OtherStringType> { \
+ public: \
+ static ::std::string Format(CharType* value) { \
+ return ::testing::PrintToString(value); \
+ } \
+ }
+
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string);
+
+#if GTEST_HAS_GLOBAL_STRING
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::string);
+#endif
+
+#if GTEST_HAS_GLOBAL_WSTRING
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::wstring);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::wstring);
+#endif
+
+#if GTEST_HAS_STD_WSTRING
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring);
+#endif
+
+#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_
+
+// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc)
+// operand to be used in a failure message. The type (but not value)
+// of the other operand may affect the format. This allows us to
+// print a char* as a raw pointer when it is compared against another
+// char* or void*, and print it as a C string when it is compared
+// against an std::string object, for example.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+template <typename T1, typename T2>
+std::string FormatForComparisonFailureMessage(
+ const T1& value, const T2& /* other_operand */) {
+ return FormatForComparison<T1, T2>::Format(value);
+}
+
+// The helper function for {ASSERT|EXPECT}_EQ.
+template <typename T1, typename T2>
+AssertionResult CmpHelperEQ(const char* expected_expression,
+ const char* actual_expression,
+ const T1& expected,
+ const T2& actual) {
+#ifdef _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4389) // Temporarily disables warning on
+ // signed/unsigned mismatch.
+#endif
+
+ if (expected == actual) {
+ return AssertionSuccess();
+ }
+
+#ifdef _MSC_VER
+# pragma warning(pop) // Restores the warning state.
+#endif
+
+ return EqFailure(expected_expression,
+ actual_expression,
+ FormatForComparisonFailureMessage(expected, actual),
+ FormatForComparisonFailureMessage(actual, expected),
+ false);
+}
+
+// With this overloaded version, we allow anonymous enums to be used
+// in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous enums
+// can be implicitly cast to BiggestInt.
+GTEST_API_ AssertionResult CmpHelperEQ(const char* expected_expression,
+ const char* actual_expression,
+ BiggestInt expected,
+ BiggestInt actual);
+
+// The helper class for {ASSERT|EXPECT}_EQ. The template argument
+// lhs_is_null_literal is true iff the first argument to ASSERT_EQ()
+// is a null pointer literal. The following default implementation is
+// for lhs_is_null_literal being false.
+template <bool lhs_is_null_literal>
+class EqHelper {
+ public:
+ // This templatized version is for the general case.
+ template <typename T1, typename T2>
+ static AssertionResult Compare(const char* expected_expression,
+ const char* actual_expression,
+ const T1& expected,
+ const T2& actual) {
+ return CmpHelperEQ(expected_expression, actual_expression, expected,
+ actual);
+ }
+
+ // With this overloaded version, we allow anonymous enums to be used
+ // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous
+ // enums can be implicitly cast to BiggestInt.
+ //
+ // Even though its body looks the same as the above version, we
+ // cannot merge the two, as it will make anonymous enums unhappy.
+ static AssertionResult Compare(const char* expected_expression,
+ const char* actual_expression,
+ BiggestInt expected,
+ BiggestInt actual) {
+ return CmpHelperEQ(expected_expression, actual_expression, expected,
+ actual);
+ }
+};
+
+// This specialization is used when the first argument to ASSERT_EQ()
+// is a null pointer literal, like NULL, false, or 0.
+template <>
+class EqHelper<true> {
+ public:
+ // We define two overloaded versions of Compare(). The first
+ // version will be picked when the second argument to ASSERT_EQ() is
+ // NOT a pointer, e.g. ASSERT_EQ(0, AnIntFunction()) or
+ // EXPECT_EQ(false, a_bool).
+ template <typename T1, typename T2>
+ static AssertionResult Compare(
+ const char* expected_expression,
+ const char* actual_expression,
+ const T1& expected,
+ const T2& actual,
+ // The following line prevents this overload from being considered if T2
+ // is not a pointer type. We need this because ASSERT_EQ(NULL, my_ptr)
+ // expands to Compare("", "", NULL, my_ptr), which requires a conversion
+ // to match the Secret* in the other overload, which would otherwise make
+ // this template match better.
+ typename EnableIf<!is_pointer<T2>::value>::type* = 0) {
+ return CmpHelperEQ(expected_expression, actual_expression, expected,
+ actual);
+ }
+
+ // This version will be picked when the second argument to ASSERT_EQ() is a
+ // pointer, e.g. ASSERT_EQ(NULL, a_pointer).
+ template <typename T>
+ static AssertionResult Compare(
+ const char* expected_expression,
+ const char* actual_expression,
+ // We used to have a second template parameter instead of Secret*. That
+ // template parameter would deduce to 'long', making this a better match
+ // than the first overload even without the first overload's EnableIf.
+ // Unfortunately, gcc with -Wconversion-null warns when "passing NULL to
+ // non-pointer argument" (even a deduced integral argument), so the old
+ // implementation caused warnings in user code.
+ Secret* /* expected (NULL) */,
+ T* actual) {
+ // We already know that 'expected' is a null pointer.
+ return CmpHelperEQ(expected_expression, actual_expression,
+ static_cast<T*>(NULL), actual);
+ }
+};
+
+// A macro for implementing the helper functions needed to implement
+// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste
+// of similar code.
+//
+// For each templatized helper function, we also define an overloaded
+// version for BiggestInt in order to reduce code bloat and allow
+// anonymous enums to be used with {ASSERT|EXPECT}_?? when compiled
+// with gcc 4.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+#define GTEST_IMPL_CMP_HELPER_(op_name, op)\
+template <typename T1, typename T2>\
+AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \
+ const T1& val1, const T2& val2) {\
+ if (val1 op val2) {\
+ return AssertionSuccess();\
+ } else {\
+ return AssertionFailure() \
+ << "Expected: (" << expr1 << ") " #op " (" << expr2\
+ << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\
+ << " vs " << FormatForComparisonFailureMessage(val2, val1);\
+ }\
+}\
+GTEST_API_ AssertionResult CmpHelper##op_name(\
+ const char* expr1, const char* expr2, BiggestInt val1, BiggestInt val2)
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+
+// Implements the helper function for {ASSERT|EXPECT}_NE
+GTEST_IMPL_CMP_HELPER_(NE, !=);
+// Implements the helper function for {ASSERT|EXPECT}_LE
+GTEST_IMPL_CMP_HELPER_(LE, <=);
+// Implements the helper function for {ASSERT|EXPECT}_LT
+GTEST_IMPL_CMP_HELPER_(LT, <);
+// Implements the helper function for {ASSERT|EXPECT}_GE
+GTEST_IMPL_CMP_HELPER_(GE, >=);
+// Implements the helper function for {ASSERT|EXPECT}_GT
+GTEST_IMPL_CMP_HELPER_(GT, >);
+
+#undef GTEST_IMPL_CMP_HELPER_
+
+// The helper function for {ASSERT|EXPECT}_STREQ.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression,
+ const char* actual_expression,
+ const char* expected,
+ const char* actual);
+
+// The helper function for {ASSERT|EXPECT}_STRCASEEQ.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression,
+ const char* actual_expression,
+ const char* expected,
+ const char* actual);
+
+// The helper function for {ASSERT|EXPECT}_STRNE.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression,
+ const char* s2_expression,
+ const char* s1,
+ const char* s2);
+
+// The helper function for {ASSERT|EXPECT}_STRCASENE.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression,
+ const char* s2_expression,
+ const char* s1,
+ const char* s2);
+
+
+// Helper function for *_STREQ on wide strings.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression,
+ const char* actual_expression,
+ const wchar_t* expected,
+ const wchar_t* actual);
+
+// Helper function for *_STRNE on wide strings.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression,
+ const char* s2_expression,
+ const wchar_t* s1,
+ const wchar_t* s2);
+
+} // namespace internal
+
+// IsSubstring() and IsNotSubstring() are intended to be used as the
+// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by
+// themselves. They check whether needle is a substring of haystack
+// (NULL is considered a substring of itself only), and return an
+// appropriate error message when they fail.
+//
+// The {needle,haystack}_expr arguments are the stringified
+// expressions that generated the two real arguments.
+GTEST_API_ AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const char* needle, const char* haystack);
+GTEST_API_ AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const wchar_t* needle, const wchar_t* haystack);
+GTEST_API_ AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const char* needle, const char* haystack);
+GTEST_API_ AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const wchar_t* needle, const wchar_t* haystack);
+GTEST_API_ AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::string& needle, const ::std::string& haystack);
+GTEST_API_ AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::string& needle, const ::std::string& haystack);
+
+#if GTEST_HAS_STD_WSTRING
+GTEST_API_ AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::wstring& needle, const ::std::wstring& haystack);
+GTEST_API_ AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::wstring& needle, const ::std::wstring& haystack);
+#endif // GTEST_HAS_STD_WSTRING
+
+namespace internal {
+
+// Helper template function for comparing floating-points.
+//
+// Template parameter:
+//
+// RawType: the raw floating-point type (either float or double)
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+template <typename RawType>
+AssertionResult CmpHelperFloatingPointEQ(const char* expected_expression,
+ const char* actual_expression,
+ RawType expected,
+ RawType actual) {
+ const FloatingPoint<RawType> lhs(expected), rhs(actual);
+
+ if (lhs.AlmostEquals(rhs)) {
+ return AssertionSuccess();
+ }
+
+ ::std::stringstream expected_ss;
+ expected_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
+ << expected;
+
+ ::std::stringstream actual_ss;
+ actual_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
+ << actual;
+
+ return EqFailure(expected_expression,
+ actual_expression,
+ StringStreamToString(&expected_ss),
+ StringStreamToString(&actual_ss),
+ false);
+}
+
+// Helper function for implementing ASSERT_NEAR.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1,
+ const char* expr2,
+ const char* abs_error_expr,
+ double val1,
+ double val2,
+ double abs_error);
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+// A class that enables one to stream messages to assertion macros
+class GTEST_API_ AssertHelper {
+ public:
+ // Constructor.
+ AssertHelper(TestPartResult::Type type,
+ const char* file,
+ int line,
+ const char* message);
+ ~AssertHelper();
+
+ // Message assignment is a semantic trick to enable assertion
+ // streaming; see the GTEST_MESSAGE_ macro below.
+ void operator=(const Message& message) const;
+
+ private:
+ // We put our data in a struct so that the size of the AssertHelper class can
+ // be as small as possible. This is important because gcc is incapable of
+ // re-using stack space even for temporary variables, so every EXPECT_EQ
+ // reserves stack space for another AssertHelper.
+ struct AssertHelperData {
+ AssertHelperData(TestPartResult::Type t,
+ const char* srcfile,
+ int line_num,
+ const char* msg)
+ : type(t), file(srcfile), line(line_num), message(msg) { }
+
+ TestPartResult::Type const type;
+ const char* const file;
+ int const line;
+ std::string const message;
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelperData);
+ };
+
+ AssertHelperData* const data_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelper);
+};
+
+} // namespace internal
+
+#if GTEST_HAS_PARAM_TEST
+// The pure interface class that all value-parameterized tests inherit from.
+// A value-parameterized class must inherit from both ::testing::Test and
+// ::testing::WithParamInterface. In most cases that just means inheriting
+// from ::testing::TestWithParam, but more complicated test hierarchies
+// may need to inherit from Test and WithParamInterface at different levels.
+//
+// This interface has support for accessing the test parameter value via
+// the GetParam() method.
+//
+// Use it with one of the parameter generator defining functions, like Range(),
+// Values(), ValuesIn(), Bool(), and Combine().
+//
+// class FooTest : public ::testing::TestWithParam<int> {
+// protected:
+// FooTest() {
+// // Can use GetParam() here.
+// }
+// virtual ~FooTest() {
+// // Can use GetParam() here.
+// }
+// virtual void SetUp() {
+// // Can use GetParam() here.
+// }
+// virtual void TearDown {
+// // Can use GetParam() here.
+// }
+// };
+// TEST_P(FooTest, DoesBar) {
+// // Can use GetParam() method here.
+// Foo foo;
+// ASSERT_TRUE(foo.DoesBar(GetParam()));
+// }
+// INSTANTIATE_TEST_CASE_P(OneToTenRange, FooTest, ::testing::Range(1, 10));
+
+template <typename T>
+class WithParamInterface {
+ public:
+ typedef T ParamType;
+ virtual ~WithParamInterface() {}
+
+ // The current parameter value. Is also available in the test fixture's
+ // constructor. This member function is non-static, even though it only
+ // references static data, to reduce the opportunity for incorrect uses
+ // like writing 'WithParamInterface<bool>::GetParam()' for a test that
+ // uses a fixture whose parameter type is int.
+ const ParamType& GetParam() const {
+ GTEST_CHECK_(parameter_ != NULL)
+ << "GetParam() can only be called inside a value-parameterized test "
+ << "-- did you intend to write TEST_P instead of TEST_F?";
+ return *parameter_;
+ }
+
+ private:
+ // Sets parameter value. The caller is responsible for making sure the value
+ // remains alive and unchanged throughout the current test.
+ static void SetParam(const ParamType* parameter) {
+ parameter_ = parameter;
+ }
+
+ // Static value used for accessing parameter during a test lifetime.
+ static const ParamType* parameter_;
+
+ // TestClass must be a subclass of WithParamInterface<T> and Test.
+ template <class TestClass> friend class internal::ParameterizedTestFactory;
+};
+
+template <typename T>
+const T* WithParamInterface<T>::parameter_ = NULL;
+
+// Most value-parameterized classes can ignore the existence of
+// WithParamInterface, and can just inherit from ::testing::TestWithParam.
+
+template <typename T>
+class TestWithParam : public Test, public WithParamInterface<T> {
+};
+
+#endif // GTEST_HAS_PARAM_TEST
+
+// Macros for indicating success/failure in test code.
+
+// ADD_FAILURE unconditionally adds a failure to the current test.
+// SUCCEED generates a success - it doesn't automatically make the
+// current test successful, as a test is only successful when it has
+// no failure.
+//
+// EXPECT_* verifies that a certain condition is satisfied. If not,
+// it behaves like ADD_FAILURE. In particular:
+//
+// EXPECT_TRUE verifies that a Boolean condition is true.
+// EXPECT_FALSE verifies that a Boolean condition is false.
+//
+// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except
+// that they will also abort the current function on failure. People
+// usually want the fail-fast behavior of FAIL and ASSERT_*, but those
+// writing data-driven tests often find themselves using ADD_FAILURE
+// and EXPECT_* more.
+
+// Generates a nonfatal failure with a generic message.
+#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed")
+
+// Generates a nonfatal failure at the given source file location with
+// a generic message.
+#define ADD_FAILURE_AT(file, line) \
+ GTEST_MESSAGE_AT_(file, line, "Failed", \
+ ::testing::TestPartResult::kNonFatalFailure)
+
+// Generates a fatal failure with a generic message.
+#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed")
+
+// Define this macro to 1 to omit the definition of FAIL(), which is a
+// generic name and clashes with some other libraries.
+#if !GTEST_DONT_DEFINE_FAIL
+# define FAIL() GTEST_FAIL()
+#endif
+
+// Generates a success with a generic message.
+#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded")
+
+// Define this macro to 1 to omit the definition of SUCCEED(), which
+// is a generic name and clashes with some other libraries.
+#if !GTEST_DONT_DEFINE_SUCCEED
+# define SUCCEED() GTEST_SUCCEED()
+#endif
+
+// Macros for testing exceptions.
+//
+// * {ASSERT|EXPECT}_THROW(statement, expected_exception):
+// Tests that the statement throws the expected exception.
+// * {ASSERT|EXPECT}_NO_THROW(statement):
+// Tests that the statement doesn't throw any exception.
+// * {ASSERT|EXPECT}_ANY_THROW(statement):
+// Tests that the statement throws an exception.
+
+#define EXPECT_THROW(statement, expected_exception) \
+ GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_NO_THROW(statement) \
+ GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_ANY_THROW(statement) \
+ GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_THROW(statement, expected_exception) \
+ GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_)
+#define ASSERT_NO_THROW(statement) \
+ GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_)
+#define ASSERT_ANY_THROW(statement) \
+ GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_)
+
+// Boolean assertions. Condition can be either a Boolean expression or an
+// AssertionResult. For more information on how to use AssertionResult with
+// these macros see comments on that class.
+#define EXPECT_TRUE(condition) \
+ GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
+ GTEST_NONFATAL_FAILURE_)
+#define EXPECT_FALSE(condition) \
+ GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
+ GTEST_NONFATAL_FAILURE_)
+#define ASSERT_TRUE(condition) \
+ GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
+ GTEST_FATAL_FAILURE_)
+#define ASSERT_FALSE(condition) \
+ GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
+ GTEST_FATAL_FAILURE_)
+
+// Includes the auto-generated header that implements a family of
+// generic predicate assertion macros.
+#include "gtest/gtest_pred_impl.h"
+
+// Macros for testing equalities and inequalities.
+//
+// * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual
+// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2
+// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2
+// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2
+// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2
+// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2
+//
+// When they are not, Google Test prints both the tested expressions and
+// their actual values. The values must be compatible built-in types,
+// or you will get a compiler error. By "compatible" we mean that the
+// values can be compared by the respective operator.
+//
+// Note:
+//
+// 1. It is possible to make a user-defined type work with
+// {ASSERT|EXPECT}_??(), but that requires overloading the
+// comparison operators and is thus discouraged by the Google C++
+// Usage Guide. Therefore, you are advised to use the
+// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are
+// equal.
+//
+// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on
+// pointers (in particular, C strings). Therefore, if you use it
+// with two C strings, you are testing how their locations in memory
+// are related, not how their content is related. To compare two C
+// strings by content, use {ASSERT|EXPECT}_STR*().
+//
+// 3. {ASSERT|EXPECT}_EQ(expected, actual) is preferred to
+// {ASSERT|EXPECT}_TRUE(expected == actual), as the former tells you
+// what the actual value is when it fails, and similarly for the
+// other comparisons.
+//
+// 4. Do not depend on the order in which {ASSERT|EXPECT}_??()
+// evaluate their arguments, which is undefined.
+//
+// 5. These macros evaluate their arguments exactly once.
+//
+// Examples:
+//
+// EXPECT_NE(5, Foo());
+// EXPECT_EQ(NULL, a_pointer);
+// ASSERT_LT(i, array_size);
+// ASSERT_GT(records.size(), 0) << "There is no record left.";
+
+#define EXPECT_EQ(expected, actual) \
+ EXPECT_PRED_FORMAT2(::testing::internal:: \
+ EqHelper<GTEST_IS_NULL_LITERAL_(expected)>::Compare, \
+ expected, actual)
+#define EXPECT_NE(expected, actual) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, expected, actual)
+#define EXPECT_LE(val1, val2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2)
+#define EXPECT_LT(val1, val2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2)
+#define EXPECT_GE(val1, val2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2)
+#define EXPECT_GT(val1, val2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2)
+
+#define GTEST_ASSERT_EQ(expected, actual) \
+ ASSERT_PRED_FORMAT2(::testing::internal:: \
+ EqHelper<GTEST_IS_NULL_LITERAL_(expected)>::Compare, \
+ expected, actual)
+#define GTEST_ASSERT_NE(val1, val2) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2)
+#define GTEST_ASSERT_LE(val1, val2) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2)
+#define GTEST_ASSERT_LT(val1, val2) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2)
+#define GTEST_ASSERT_GE(val1, val2) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2)
+#define GTEST_ASSERT_GT(val1, val2) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2)
+
+// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of
+// ASSERT_XY(), which clashes with some users' own code.
+
+#if !GTEST_DONT_DEFINE_ASSERT_EQ
+# define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_NE
+# define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_LE
+# define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_LT
+# define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_GE
+# define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_GT
+# define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2)
+#endif
+
+// C-string Comparisons. All tests treat NULL and any non-NULL string
+// as different. Two NULLs are equal.
+//
+// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2
+// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2
+// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case
+// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case
+//
+// For wide or narrow string objects, you can use the
+// {ASSERT|EXPECT}_??() macros.
+//
+// Don't depend on the order in which the arguments are evaluated,
+// which is undefined.
+//
+// These macros evaluate their arguments exactly once.
+
+#define EXPECT_STREQ(expected, actual) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual)
+#define EXPECT_STRNE(s1, s2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2)
+#define EXPECT_STRCASEEQ(expected, actual) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual)
+#define EXPECT_STRCASENE(s1, s2)\
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2)
+
+#define ASSERT_STREQ(expected, actual) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual)
+#define ASSERT_STRNE(s1, s2) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2)
+#define ASSERT_STRCASEEQ(expected, actual) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual)
+#define ASSERT_STRCASENE(s1, s2)\
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2)
+
+// Macros for comparing floating-point numbers.
+//
+// * {ASSERT|EXPECT}_FLOAT_EQ(expected, actual):
+// Tests that two float values are almost equal.
+// * {ASSERT|EXPECT}_DOUBLE_EQ(expected, actual):
+// Tests that two double values are almost equal.
+// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error):
+// Tests that v1 and v2 are within the given distance to each other.
+//
+// Google Test uses ULP-based comparison to automatically pick a default
+// error bound that is appropriate for the operands. See the
+// FloatingPoint template class in gtest-internal.h if you are
+// interested in the implementation details.
+
+#define EXPECT_FLOAT_EQ(expected, actual)\
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<float>, \
+ expected, actual)
+
+#define EXPECT_DOUBLE_EQ(expected, actual)\
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<double>, \
+ expected, actual)
+
+#define ASSERT_FLOAT_EQ(expected, actual)\
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<float>, \
+ expected, actual)
+
+#define ASSERT_DOUBLE_EQ(expected, actual)\
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<double>, \
+ expected, actual)
+
+#define EXPECT_NEAR(val1, val2, abs_error)\
+ EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \
+ val1, val2, abs_error)
+
+#define ASSERT_NEAR(val1, val2, abs_error)\
+ ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \
+ val1, val2, abs_error)
+
+// These predicate format functions work on floating-point values, and
+// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g.
+//
+// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0);
+
+// Asserts that val1 is less than, or almost equal to, val2. Fails
+// otherwise. In particular, it fails if either val1 or val2 is NaN.
+GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2,
+ float val1, float val2);
+GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2,
+ double val1, double val2);
+
+
+#if GTEST_OS_WINDOWS
+
+// Macros that test for HRESULT failure and success, these are only useful
+// on Windows, and rely on Windows SDK macros and APIs to compile.
+//
+// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr)
+//
+// When expr unexpectedly fails or succeeds, Google Test prints the
+// expected result and the actual result with both a human-readable
+// string representation of the error, if available, as well as the
+// hex result code.
+# define EXPECT_HRESULT_SUCCEEDED(expr) \
+ EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr))
+
+# define ASSERT_HRESULT_SUCCEEDED(expr) \
+ ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr))
+
+# define EXPECT_HRESULT_FAILED(expr) \
+ EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr))
+
+# define ASSERT_HRESULT_FAILED(expr) \
+ ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr))
+
+#endif // GTEST_OS_WINDOWS
+
+// Macros that execute statement and check that it doesn't generate new fatal
+// failures in the current thread.
+//
+// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement);
+//
+// Examples:
+//
+// EXPECT_NO_FATAL_FAILURE(Process());
+// ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed";
+//
+#define ASSERT_NO_FATAL_FAILURE(statement) \
+ GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_)
+#define EXPECT_NO_FATAL_FAILURE(statement) \
+ GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_)
+
+// Causes a trace (including the source file path, the current line
+// number, and the given message) to be included in every test failure
+// message generated by code in the current scope. The effect is
+// undone when the control leaves the current scope.
+//
+// The message argument can be anything streamable to std::ostream.
+//
+// In the implementation, we include the current line number as part
+// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s
+// to appear in the same block - as long as they are on different
+// lines.
+#define SCOPED_TRACE(message) \
+ ::testing::internal::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\
+ __FILE__, __LINE__, ::testing::Message() << (message))
+
+// Compile-time assertion for type equality.
+// StaticAssertTypeEq<type1, type2>() compiles iff type1 and type2 are
+// the same type. The value it returns is not interesting.
+//
+// Instead of making StaticAssertTypeEq a class template, we make it a
+// function template that invokes a helper class template. This
+// prevents a user from misusing StaticAssertTypeEq<T1, T2> by
+// defining objects of that type.
+//
+// CAVEAT:
+//
+// When used inside a method of a class template,
+// StaticAssertTypeEq<T1, T2>() is effective ONLY IF the method is
+// instantiated. For example, given:
+//
+// template <typename T> class Foo {
+// public:
+// void Bar() { testing::StaticAssertTypeEq<int, T>(); }
+// };
+//
+// the code:
+//
+// void Test1() { Foo<bool> foo; }
+//
+// will NOT generate a compiler error, as Foo<bool>::Bar() is never
+// actually instantiated. Instead, you need:
+//
+// void Test2() { Foo<bool> foo; foo.Bar(); }
+//
+// to cause a compiler error.
+template <typename T1, typename T2>
+bool StaticAssertTypeEq() {
+ (void)internal::StaticAssertTypeEqHelper<T1, T2>();
+ return true;
+}
+
+// Defines a test.
+//
+// The first parameter is the name of the test case, and the second
+// parameter is the name of the test within the test case.
+//
+// The convention is to end the test case name with "Test". For
+// example, a test case for the Foo class can be named FooTest.
+//
+// The user should put his test code between braces after using this
+// macro. Example:
+//
+// TEST(FooTest, InitializesCorrectly) {
+// Foo foo;
+// EXPECT_TRUE(foo.StatusIsOK());
+// }
+
+// Note that we call GetTestTypeId() instead of GetTypeId<
+// ::testing::Test>() here to get the type ID of testing::Test. This
+// is to work around a suspected linker bug when using Google Test as
+// a framework on Mac OS X. The bug causes GetTypeId<
+// ::testing::Test>() to return different values depending on whether
+// the call is from the Google Test framework itself or from user test
+// code. GetTestTypeId() is guaranteed to always return the same
+// value, as it always calls GetTypeId<>() from the Google Test
+// framework.
+#define GTEST_TEST(test_case_name, test_name)\
+ GTEST_TEST_(test_case_name, test_name, \
+ ::testing::Test, ::testing::internal::GetTestTypeId())
+
+// Define this macro to 1 to omit the definition of TEST(), which
+// is a generic name and clashes with some other libraries.
+#if !GTEST_DONT_DEFINE_TEST
+# define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name)
+#endif
+
+// Defines a test that uses a test fixture.
+//
+// The first parameter is the name of the test fixture class, which
+// also doubles as the test case name. The second parameter is the
+// name of the test within the test case.
+//
+// A test fixture class must be declared earlier. The user should put
+// his test code between braces after using this macro. Example:
+//
+// class FooTest : public testing::Test {
+// protected:
+// virtual void SetUp() { b_.AddElement(3); }
+//
+// Foo a_;
+// Foo b_;
+// };
+//
+// TEST_F(FooTest, InitializesCorrectly) {
+// EXPECT_TRUE(a_.StatusIsOK());
+// }
+//
+// TEST_F(FooTest, ReturnsElementCountCorrectly) {
+// EXPECT_EQ(0, a_.size());
+// EXPECT_EQ(1, b_.size());
+// }
+
+#define TEST_F(test_fixture, test_name)\
+ GTEST_TEST_(test_fixture, test_name, test_fixture, \
+ ::testing::internal::GetTypeId<test_fixture>())
+
+} // namespace testing
+
+// Use this function in main() to run all tests. It returns 0 if all
+// tests are successful, or 1 otherwise.
+//
+// RUN_ALL_TESTS() should be invoked after the command line has been
+// parsed by InitGoogleTest().
+//
+// This function was formerly a macro; thus, it is in the global
+// namespace and has an all-caps name.
+int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_;
+
+inline int RUN_ALL_TESTS() {
+ return ::testing::UnitTest::GetInstance()->Run();
+}
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_H_
diff --git a/extern/gtest/include/gtest/gtest_pred_impl.h b/extern/gtest/include/gtest/gtest_pred_impl.h
new file mode 100644
index 00000000000..30ae712f50e
--- /dev/null
+++ b/extern/gtest/include/gtest/gtest_pred_impl.h
@@ -0,0 +1,358 @@
+// Copyright 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file is AUTOMATICALLY GENERATED on 10/31/2011 by command
+// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND!
+//
+// Implements a family of generic predicate assertion macros.
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
+#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
+
+// Makes sure this header is not included before gtest.h.
+#ifndef GTEST_INCLUDE_GTEST_GTEST_H_
+# error Do not include gtest_pred_impl.h directly. Include gtest.h instead.
+#endif // GTEST_INCLUDE_GTEST_GTEST_H_
+
+// This header implements a family of generic predicate assertion
+// macros:
+//
+// ASSERT_PRED_FORMAT1(pred_format, v1)
+// ASSERT_PRED_FORMAT2(pred_format, v1, v2)
+// ...
+//
+// where pred_format is a function or functor that takes n (in the
+// case of ASSERT_PRED_FORMATn) values and their source expression
+// text, and returns a testing::AssertionResult. See the definition
+// of ASSERT_EQ in gtest.h for an example.
+//
+// If you don't care about formatting, you can use the more
+// restrictive version:
+//
+// ASSERT_PRED1(pred, v1)
+// ASSERT_PRED2(pred, v1, v2)
+// ...
+//
+// where pred is an n-ary function or functor that returns bool,
+// and the values v1, v2, ..., must support the << operator for
+// streaming to std::ostream.
+//
+// We also define the EXPECT_* variations.
+//
+// For now we only support predicates whose arity is at most 5.
+// Please email googletestframework@googlegroups.com if you need
+// support for higher arities.
+
+// GTEST_ASSERT_ is the basic statement to which all of the assertions
+// in this file reduce. Don't use this in your code.
+
+#define GTEST_ASSERT_(expression, on_failure) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (const ::testing::AssertionResult gtest_ar = (expression)) \
+ ; \
+ else \
+ on_failure(gtest_ar.failure_message())
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use
+// this in your code.
+template <typename Pred,
+ typename T1>
+AssertionResult AssertPred1Helper(const char* pred_text,
+ const char* e1,
+ Pred pred,
+ const T1& v1) {
+ if (pred(v1)) return AssertionSuccess();
+
+ return AssertionFailure() << pred_text << "("
+ << e1 << ") evaluates to false, where"
+ << "\n" << e1 << " evaluates to " << v1;
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\
+ GTEST_ASSERT_(pred_format(#v1, v1), \
+ on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use
+// this in your code.
+#define GTEST_PRED1_(pred, v1, on_failure)\
+ GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \
+ #v1, \
+ pred, \
+ v1), on_failure)
+
+// Unary predicate assertion macros.
+#define EXPECT_PRED_FORMAT1(pred_format, v1) \
+ GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED1(pred, v1) \
+ GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT1(pred_format, v1) \
+ GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED1(pred, v1) \
+ GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_)
+
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use
+// this in your code.
+template <typename Pred,
+ typename T1,
+ typename T2>
+AssertionResult AssertPred2Helper(const char* pred_text,
+ const char* e1,
+ const char* e2,
+ Pred pred,
+ const T1& v1,
+ const T2& v2) {
+ if (pred(v1, v2)) return AssertionSuccess();
+
+ return AssertionFailure() << pred_text << "("
+ << e1 << ", "
+ << e2 << ") evaluates to false, where"
+ << "\n" << e1 << " evaluates to " << v1
+ << "\n" << e2 << " evaluates to " << v2;
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\
+ GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \
+ on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use
+// this in your code.
+#define GTEST_PRED2_(pred, v1, v2, on_failure)\
+ GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \
+ #v1, \
+ #v2, \
+ pred, \
+ v1, \
+ v2), on_failure)
+
+// Binary predicate assertion macros.
+#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \
+ GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED2(pred, v1, v2) \
+ GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \
+ GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED2(pred, v1, v2) \
+ GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_)
+
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use
+// this in your code.
+template <typename Pred,
+ typename T1,
+ typename T2,
+ typename T3>
+AssertionResult AssertPred3Helper(const char* pred_text,
+ const char* e1,
+ const char* e2,
+ const char* e3,
+ Pred pred,
+ const T1& v1,
+ const T2& v2,
+ const T3& v3) {
+ if (pred(v1, v2, v3)) return AssertionSuccess();
+
+ return AssertionFailure() << pred_text << "("
+ << e1 << ", "
+ << e2 << ", "
+ << e3 << ") evaluates to false, where"
+ << "\n" << e1 << " evaluates to " << v1
+ << "\n" << e2 << " evaluates to " << v2
+ << "\n" << e3 << " evaluates to " << v3;
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\
+ GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \
+ on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use
+// this in your code.
+#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\
+ GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \
+ #v1, \
+ #v2, \
+ #v3, \
+ pred, \
+ v1, \
+ v2, \
+ v3), on_failure)
+
+// Ternary predicate assertion macros.
+#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \
+ GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED3(pred, v1, v2, v3) \
+ GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \
+ GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED3(pred, v1, v2, v3) \
+ GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_)
+
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use
+// this in your code.
+template <typename Pred,
+ typename T1,
+ typename T2,
+ typename T3,
+ typename T4>
+AssertionResult AssertPred4Helper(const char* pred_text,
+ const char* e1,
+ const char* e2,
+ const char* e3,
+ const char* e4,
+ Pred pred,
+ const T1& v1,
+ const T2& v2,
+ const T3& v3,
+ const T4& v4) {
+ if (pred(v1, v2, v3, v4)) return AssertionSuccess();
+
+ return AssertionFailure() << pred_text << "("
+ << e1 << ", "
+ << e2 << ", "
+ << e3 << ", "
+ << e4 << ") evaluates to false, where"
+ << "\n" << e1 << " evaluates to " << v1
+ << "\n" << e2 << " evaluates to " << v2
+ << "\n" << e3 << " evaluates to " << v3
+ << "\n" << e4 << " evaluates to " << v4;
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\
+ GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \
+ on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use
+// this in your code.
+#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\
+ GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \
+ #v1, \
+ #v2, \
+ #v3, \
+ #v4, \
+ pred, \
+ v1, \
+ v2, \
+ v3, \
+ v4), on_failure)
+
+// 4-ary predicate assertion macros.
+#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \
+ GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED4(pred, v1, v2, v3, v4) \
+ GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \
+ GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED4(pred, v1, v2, v3, v4) \
+ GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_)
+
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use
+// this in your code.
+template <typename Pred,
+ typename T1,
+ typename T2,
+ typename T3,
+ typename T4,
+ typename T5>
+AssertionResult AssertPred5Helper(const char* pred_text,
+ const char* e1,
+ const char* e2,
+ const char* e3,
+ const char* e4,
+ const char* e5,
+ Pred pred,
+ const T1& v1,
+ const T2& v2,
+ const T3& v3,
+ const T4& v4,
+ const T5& v5) {
+ if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess();
+
+ return AssertionFailure() << pred_text << "("
+ << e1 << ", "
+ << e2 << ", "
+ << e3 << ", "
+ << e4 << ", "
+ << e5 << ") evaluates to false, where"
+ << "\n" << e1 << " evaluates to " << v1
+ << "\n" << e2 << " evaluates to " << v2
+ << "\n" << e3 << " evaluates to " << v3
+ << "\n" << e4 << " evaluates to " << v4
+ << "\n" << e5 << " evaluates to " << v5;
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\
+ GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \
+ on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use
+// this in your code.
+#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\
+ GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \
+ #v1, \
+ #v2, \
+ #v3, \
+ #v4, \
+ #v5, \
+ pred, \
+ v1, \
+ v2, \
+ v3, \
+ v4, \
+ v5), on_failure)
+
+// 5-ary predicate assertion macros.
+#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \
+ GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \
+ GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \
+ GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \
+ GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_)
+
+
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
diff --git a/extern/gtest/include/gtest/gtest_prod.h b/extern/gtest/include/gtest/gtest_prod.h
new file mode 100644
index 00000000000..da80ddc6c70
--- /dev/null
+++ b/extern/gtest/include/gtest/gtest_prod.h
@@ -0,0 +1,58 @@
+// Copyright 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+//
+// Google C++ Testing Framework definitions useful in production code.
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_
+#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_
+
+// When you need to test the private or protected members of a class,
+// use the FRIEND_TEST macro to declare your tests as friends of the
+// class. For example:
+//
+// class MyClass {
+// private:
+// void MyMethod();
+// FRIEND_TEST(MyClassTest, MyMethod);
+// };
+//
+// class MyClassTest : public testing::Test {
+// // ...
+// };
+//
+// TEST_F(MyClassTest, MyMethod) {
+// // Can call MyClass::MyMethod() here.
+// }
+
+#define FRIEND_TEST(test_case_name, test_name)\
+friend class test_case_name##_##test_name##_Test
+
+#endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-death-test-internal.h b/extern/gtest/include/gtest/internal/gtest-death-test-internal.h
new file mode 100644
index 00000000000..2b3a78f5bf8
--- /dev/null
+++ b/extern/gtest/include/gtest/internal/gtest-death-test-internal.h
@@ -0,0 +1,319 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file defines internal utilities needed for implementing
+// death tests. They are subject to change without notice.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
+
+#include "gtest/internal/gtest-internal.h"
+
+#include <stdio.h>
+
+namespace testing {
+namespace internal {
+
+GTEST_DECLARE_string_(internal_run_death_test);
+
+// Names of the flags (needed for parsing Google Test flags).
+const char kDeathTestStyleFlag[] = "death_test_style";
+const char kDeathTestUseFork[] = "death_test_use_fork";
+const char kInternalRunDeathTestFlag[] = "internal_run_death_test";
+
+#if GTEST_HAS_DEATH_TEST
+
+// DeathTest is a class that hides much of the complexity of the
+// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method
+// returns a concrete class that depends on the prevailing death test
+// style, as defined by the --gtest_death_test_style and/or
+// --gtest_internal_run_death_test flags.
+
+// In describing the results of death tests, these terms are used with
+// the corresponding definitions:
+//
+// exit status: The integer exit information in the format specified
+// by wait(2)
+// exit code: The integer code passed to exit(3), _exit(2), or
+// returned from main()
+class GTEST_API_ DeathTest {
+ public:
+ // Create returns false if there was an error determining the
+ // appropriate action to take for the current death test; for example,
+ // if the gtest_death_test_style flag is set to an invalid value.
+ // The LastMessage method will return a more detailed message in that
+ // case. Otherwise, the DeathTest pointer pointed to by the "test"
+ // argument is set. If the death test should be skipped, the pointer
+ // is set to NULL; otherwise, it is set to the address of a new concrete
+ // DeathTest object that controls the execution of the current test.
+ static bool Create(const char* statement, const RE* regex,
+ const char* file, int line, DeathTest** test);
+ DeathTest();
+ virtual ~DeathTest() { }
+
+ // A helper class that aborts a death test when it's deleted.
+ class ReturnSentinel {
+ public:
+ explicit ReturnSentinel(DeathTest* test) : test_(test) { }
+ ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); }
+ private:
+ DeathTest* const test_;
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ReturnSentinel);
+ } GTEST_ATTRIBUTE_UNUSED_;
+
+ // An enumeration of possible roles that may be taken when a death
+ // test is encountered. EXECUTE means that the death test logic should
+ // be executed immediately. OVERSEE means that the program should prepare
+ // the appropriate environment for a child process to execute the death
+ // test, then wait for it to complete.
+ enum TestRole { OVERSEE_TEST, EXECUTE_TEST };
+
+ // An enumeration of the three reasons that a test might be aborted.
+ enum AbortReason {
+ TEST_ENCOUNTERED_RETURN_STATEMENT,
+ TEST_THREW_EXCEPTION,
+ TEST_DID_NOT_DIE
+ };
+
+ // Assumes one of the above roles.
+ virtual TestRole AssumeRole() = 0;
+
+ // Waits for the death test to finish and returns its status.
+ virtual int Wait() = 0;
+
+ // Returns true if the death test passed; that is, the test process
+ // exited during the test, its exit status matches a user-supplied
+ // predicate, and its stderr output matches a user-supplied regular
+ // expression.
+ // The user-supplied predicate may be a macro expression rather
+ // than a function pointer or functor, or else Wait and Passed could
+ // be combined.
+ virtual bool Passed(bool exit_status_ok) = 0;
+
+ // Signals that the death test did not die as expected.
+ virtual void Abort(AbortReason reason) = 0;
+
+ // Returns a human-readable outcome message regarding the outcome of
+ // the last death test.
+ static const char* LastMessage();
+
+ static void set_last_death_test_message(const std::string& message);
+
+ private:
+ // A string containing a description of the outcome of the last death test.
+ static std::string last_death_test_message_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest);
+};
+
+// Factory interface for death tests. May be mocked out for testing.
+class DeathTestFactory {
+ public:
+ virtual ~DeathTestFactory() { }
+ virtual bool Create(const char* statement, const RE* regex,
+ const char* file, int line, DeathTest** test) = 0;
+};
+
+// A concrete DeathTestFactory implementation for normal use.
+class DefaultDeathTestFactory : public DeathTestFactory {
+ public:
+ virtual bool Create(const char* statement, const RE* regex,
+ const char* file, int line, DeathTest** test);
+};
+
+// Returns true if exit_status describes a process that was terminated
+// by a signal, or exited normally with a nonzero exit code.
+GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
+
+// Traps C++ exceptions escaping statement and reports them as test
+// failures. Note that trapping SEH exceptions is not implemented here.
+# if GTEST_HAS_EXCEPTIONS
+# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
+ try { \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ } catch (const ::std::exception& gtest_exception) { \
+ fprintf(\
+ stderr, \
+ "\n%s: Caught std::exception-derived exception escaping the " \
+ "death test statement. Exception message: %s\n", \
+ ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \
+ gtest_exception.what()); \
+ fflush(stderr); \
+ death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
+ } catch (...) { \
+ death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
+ }
+
+# else
+# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
+
+# endif
+
+// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*,
+// ASSERT_EXIT*, and EXPECT_EXIT*.
+# define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::AlwaysTrue()) { \
+ const ::testing::internal::RE& gtest_regex = (regex); \
+ ::testing::internal::DeathTest* gtest_dt; \
+ if (!::testing::internal::DeathTest::Create(#statement, &gtest_regex, \
+ __FILE__, __LINE__, &gtest_dt)) { \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
+ } \
+ if (gtest_dt != NULL) { \
+ ::testing::internal::scoped_ptr< ::testing::internal::DeathTest> \
+ gtest_dt_ptr(gtest_dt); \
+ switch (gtest_dt->AssumeRole()) { \
+ case ::testing::internal::DeathTest::OVERSEE_TEST: \
+ if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
+ } \
+ break; \
+ case ::testing::internal::DeathTest::EXECUTE_TEST: { \
+ ::testing::internal::DeathTest::ReturnSentinel \
+ gtest_sentinel(gtest_dt); \
+ GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \
+ gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \
+ break; \
+ } \
+ default: \
+ break; \
+ } \
+ } \
+ } else \
+ GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__): \
+ fail(::testing::internal::DeathTest::LastMessage())
+// The symbol "fail" here expands to something into which a message
+// can be streamed.
+
+// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in
+// NDEBUG mode. In this case we need the statements to be executed, the regex is
+// ignored, and the macro must accept a streamed message even though the message
+// is never printed.
+# define GTEST_EXECUTE_STATEMENT_(statement, regex) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::AlwaysTrue()) { \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ } else \
+ ::testing::Message()
+
+// A class representing the parsed contents of the
+// --gtest_internal_run_death_test flag, as it existed when
+// RUN_ALL_TESTS was called.
+class InternalRunDeathTestFlag {
+ public:
+ InternalRunDeathTestFlag(const std::string& a_file,
+ int a_line,
+ int an_index,
+ int a_write_fd)
+ : file_(a_file), line_(a_line), index_(an_index),
+ write_fd_(a_write_fd) {}
+
+ ~InternalRunDeathTestFlag() {
+ if (write_fd_ >= 0)
+ posix::Close(write_fd_);
+ }
+
+ const std::string& file() const { return file_; }
+ int line() const { return line_; }
+ int index() const { return index_; }
+ int write_fd() const { return write_fd_; }
+
+ private:
+ std::string file_;
+ int line_;
+ int index_;
+ int write_fd_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(InternalRunDeathTestFlag);
+};
+
+// Returns a newly created InternalRunDeathTestFlag object with fields
+// initialized from the GTEST_FLAG(internal_run_death_test) flag if
+// the flag is specified; otherwise returns NULL.
+InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag();
+
+#else // GTEST_HAS_DEATH_TEST
+
+// This macro is used for implementing macros such as
+// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where
+// death tests are not supported. Those macros must compile on such systems
+// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on
+// systems that support death tests. This allows one to write such a macro
+// on a system that does not support death tests and be sure that it will
+// compile on a death-test supporting system.
+//
+// Parameters:
+// statement - A statement that a macro such as EXPECT_DEATH would test
+// for program termination. This macro has to make sure this
+// statement is compiled but not executed, to ensure that
+// EXPECT_DEATH_IF_SUPPORTED compiles with a certain
+// parameter iff EXPECT_DEATH compiles with it.
+// regex - A regex that a macro such as EXPECT_DEATH would use to test
+// the output of statement. This parameter has to be
+// compiled but not evaluated by this macro, to ensure that
+// this macro only accepts expressions that a macro such as
+// EXPECT_DEATH would accept.
+// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED
+// and a return statement for ASSERT_DEATH_IF_SUPPORTED.
+// This ensures that ASSERT_DEATH_IF_SUPPORTED will not
+// compile inside functions where ASSERT_DEATH doesn't
+// compile.
+//
+// The branch that has an always false condition is used to ensure that
+// statement and regex are compiled (and thus syntactically correct) but
+// never executed. The unreachable code macro protects the terminator
+// statement from generating an 'unreachable code' warning in case
+// statement unconditionally returns or throws. The Message constructor at
+// the end allows the syntax of streaming additional messages into the
+// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH.
+# define GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, terminator) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::AlwaysTrue()) { \
+ GTEST_LOG_(WARNING) \
+ << "Death tests are not supported on this platform.\n" \
+ << "Statement '" #statement "' cannot be verified."; \
+ } else if (::testing::internal::AlwaysFalse()) { \
+ ::testing::internal::RE::PartialMatch(".*", (regex)); \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ terminator; \
+ } else \
+ ::testing::Message()
+
+#endif // GTEST_HAS_DEATH_TEST
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-filepath.h b/extern/gtest/include/gtest/internal/gtest-filepath.h
new file mode 100644
index 00000000000..7a13b4b0de6
--- /dev/null
+++ b/extern/gtest/include/gtest/internal/gtest-filepath.h
@@ -0,0 +1,206 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: keith.ray@gmail.com (Keith Ray)
+//
+// Google Test filepath utilities
+//
+// This header file declares classes and functions used internally by
+// Google Test. They are subject to change without notice.
+//
+// This file is #included in <gtest/internal/gtest-internal.h>.
+// Do not include this header file separately!
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
+
+#include "gtest/internal/gtest-string.h"
+
+namespace testing {
+namespace internal {
+
+// FilePath - a class for file and directory pathname manipulation which
+// handles platform-specific conventions (like the pathname separator).
+// Used for helper functions for naming files in a directory for xml output.
+// Except for Set methods, all methods are const or static, which provides an
+// "immutable value object" -- useful for peace of mind.
+// A FilePath with a value ending in a path separator ("like/this/") represents
+// a directory, otherwise it is assumed to represent a file. In either case,
+// it may or may not represent an actual file or directory in the file system.
+// Names are NOT checked for syntax correctness -- no checking for illegal
+// characters, malformed paths, etc.
+
+class GTEST_API_ FilePath {
+ public:
+ FilePath() : pathname_("") { }
+ FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { }
+
+ explicit FilePath(const std::string& pathname) : pathname_(pathname) {
+ Normalize();
+ }
+
+ FilePath& operator=(const FilePath& rhs) {
+ Set(rhs);
+ return *this;
+ }
+
+ void Set(const FilePath& rhs) {
+ pathname_ = rhs.pathname_;
+ }
+
+ const std::string& string() const { return pathname_; }
+ const char* c_str() const { return pathname_.c_str(); }
+
+ // Returns the current working directory, or "" if unsuccessful.
+ static FilePath GetCurrentDir();
+
+ // Given directory = "dir", base_name = "test", number = 0,
+ // extension = "xml", returns "dir/test.xml". If number is greater
+ // than zero (e.g., 12), returns "dir/test_12.xml".
+ // On Windows platform, uses \ as the separator rather than /.
+ static FilePath MakeFileName(const FilePath& directory,
+ const FilePath& base_name,
+ int number,
+ const char* extension);
+
+ // Given directory = "dir", relative_path = "test.xml",
+ // returns "dir/test.xml".
+ // On Windows, uses \ as the separator rather than /.
+ static FilePath ConcatPaths(const FilePath& directory,
+ const FilePath& relative_path);
+
+ // Returns a pathname for a file that does not currently exist. The pathname
+ // will be directory/base_name.extension or
+ // directory/base_name_<number>.extension if directory/base_name.extension
+ // already exists. The number will be incremented until a pathname is found
+ // that does not already exist.
+ // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
+ // There could be a race condition if two or more processes are calling this
+ // function at the same time -- they could both pick the same filename.
+ static FilePath GenerateUniqueFileName(const FilePath& directory,
+ const FilePath& base_name,
+ const char* extension);
+
+ // Returns true iff the path is "".
+ bool IsEmpty() const { return pathname_.empty(); }
+
+ // If input name has a trailing separator character, removes it and returns
+ // the name, otherwise return the name string unmodified.
+ // On Windows platform, uses \ as the separator, other platforms use /.
+ FilePath RemoveTrailingPathSeparator() const;
+
+ // Returns a copy of the FilePath with the directory part removed.
+ // Example: FilePath("path/to/file").RemoveDirectoryName() returns
+ // FilePath("file"). If there is no directory part ("just_a_file"), it returns
+ // the FilePath unmodified. If there is no file part ("just_a_dir/") it
+ // returns an empty FilePath ("").
+ // On Windows platform, '\' is the path separator, otherwise it is '/'.
+ FilePath RemoveDirectoryName() const;
+
+ // RemoveFileName returns the directory path with the filename removed.
+ // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
+ // If the FilePath is "a_file" or "/a_file", RemoveFileName returns
+ // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
+ // not have a file, like "just/a/dir/", it returns the FilePath unmodified.
+ // On Windows platform, '\' is the path separator, otherwise it is '/'.
+ FilePath RemoveFileName() const;
+
+ // Returns a copy of the FilePath with the case-insensitive extension removed.
+ // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
+ // FilePath("dir/file"). If a case-insensitive extension is not
+ // found, returns a copy of the original FilePath.
+ FilePath RemoveExtension(const char* extension) const;
+
+ // Creates directories so that path exists. Returns true if successful or if
+ // the directories already exist; returns false if unable to create
+ // directories for any reason. Will also return false if the FilePath does
+ // not represent a directory (that is, it doesn't end with a path separator).
+ bool CreateDirectoriesRecursively() const;
+
+ // Create the directory so that path exists. Returns true if successful or
+ // if the directory already exists; returns false if unable to create the
+ // directory for any reason, including if the parent directory does not
+ // exist. Not named "CreateDirectory" because that's a macro on Windows.
+ bool CreateFolder() const;
+
+ // Returns true if FilePath describes something in the file-system,
+ // either a file, directory, or whatever, and that something exists.
+ bool FileOrDirectoryExists() const;
+
+ // Returns true if pathname describes a directory in the file-system
+ // that exists.
+ bool DirectoryExists() const;
+
+ // Returns true if FilePath ends with a path separator, which indicates that
+ // it is intended to represent a directory. Returns false otherwise.
+ // This does NOT check that a directory (or file) actually exists.
+ bool IsDirectory() const;
+
+ // Returns true if pathname describes a root directory. (Windows has one
+ // root directory per disk drive.)
+ bool IsRootDirectory() const;
+
+ // Returns true if pathname describes an absolute path.
+ bool IsAbsolutePath() const;
+
+ private:
+ // Replaces multiple consecutive separators with a single separator.
+ // For example, "bar///foo" becomes "bar/foo". Does not eliminate other
+ // redundancies that might be in a pathname involving "." or "..".
+ //
+ // A pathname with multiple consecutive separators may occur either through
+ // user error or as a result of some scripts or APIs that generate a pathname
+ // with a trailing separator. On other platforms the same API or script
+ // may NOT generate a pathname with a trailing "/". Then elsewhere that
+ // pathname may have another "/" and pathname components added to it,
+ // without checking for the separator already being there.
+ // The script language and operating system may allow paths like "foo//bar"
+ // but some of the functions in FilePath will not handle that correctly. In
+ // particular, RemoveTrailingPathSeparator() only removes one separator, and
+ // it is called in CreateDirectoriesRecursively() assuming that it will change
+ // a pathname from directory syntax (trailing separator) to filename syntax.
+ //
+ // On Windows this method also replaces the alternate path separator '/' with
+ // the primary path separator '\\', so that for example "bar\\/\\foo" becomes
+ // "bar\\foo".
+
+ void Normalize();
+
+ // Returns a pointer to the last occurence of a valid path separator in
+ // the FilePath. On Windows, for example, both '/' and '\' are valid path
+ // separators. Returns NULL if no path separator was found.
+ const char* FindLastPathSeparator() const;
+
+ std::string pathname_;
+}; // class FilePath
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-internal.h b/extern/gtest/include/gtest/internal/gtest-internal.h
new file mode 100644
index 00000000000..0dcc3a3194f
--- /dev/null
+++ b/extern/gtest/include/gtest/internal/gtest-internal.h
@@ -0,0 +1,1158 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file declares functions and macros used internally by
+// Google Test. They are subject to change without notice.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
+
+#include "gtest/internal/gtest-port.h"
+
+#if GTEST_OS_LINUX
+# include <stdlib.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <unistd.h>
+#endif // GTEST_OS_LINUX
+
+#if GTEST_HAS_EXCEPTIONS
+# include <stdexcept>
+#endif
+
+#include <ctype.h>
+#include <float.h>
+#include <string.h>
+#include <iomanip>
+#include <limits>
+#include <set>
+
+#include "gtest/gtest-message.h"
+#include "gtest/internal/gtest-string.h"
+#include "gtest/internal/gtest-filepath.h"
+#include "gtest/internal/gtest-type-util.h"
+
+// Due to C++ preprocessor weirdness, we need double indirection to
+// concatenate two tokens when one of them is __LINE__. Writing
+//
+// foo ## __LINE__
+//
+// will result in the token foo__LINE__, instead of foo followed by
+// the current line number. For more details, see
+// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6
+#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar)
+#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar
+
+class ProtocolMessage;
+namespace proto2 { class Message; }
+
+namespace testing {
+
+// Forward declarations.
+
+class AssertionResult; // Result of an assertion.
+class Message; // Represents a failure message.
+class Test; // Represents a test.
+class TestInfo; // Information about a test.
+class TestPartResult; // Result of a test part.
+class UnitTest; // A collection of test cases.
+
+template <typename T>
+::std::string PrintToString(const T& value);
+
+namespace internal {
+
+struct TraceInfo; // Information about a trace point.
+class ScopedTrace; // Implements scoped trace.
+class TestInfoImpl; // Opaque implementation of TestInfo
+class UnitTestImpl; // Opaque implementation of UnitTest
+
+// How many times InitGoogleTest() has been called.
+GTEST_API_ extern int g_init_gtest_count;
+
+// The text used in failure messages to indicate the start of the
+// stack trace.
+GTEST_API_ extern const char kStackTraceMarker[];
+
+// Two overloaded helpers for checking at compile time whether an
+// expression is a null pointer literal (i.e. NULL or any 0-valued
+// compile-time integral constant). Their return values have
+// different sizes, so we can use sizeof() to test which version is
+// picked by the compiler. These helpers have no implementations, as
+// we only need their signatures.
+//
+// Given IsNullLiteralHelper(x), the compiler will pick the first
+// version if x can be implicitly converted to Secret*, and pick the
+// second version otherwise. Since Secret is a secret and incomplete
+// type, the only expression a user can write that has type Secret* is
+// a null pointer literal. Therefore, we know that x is a null
+// pointer literal if and only if the first version is picked by the
+// compiler.
+char IsNullLiteralHelper(Secret* p);
+char (&IsNullLiteralHelper(...))[2]; // NOLINT
+
+// A compile-time bool constant that is true if and only if x is a
+// null pointer literal (i.e. NULL or any 0-valued compile-time
+// integral constant).
+#ifdef GTEST_ELLIPSIS_NEEDS_POD_
+// We lose support for NULL detection where the compiler doesn't like
+// passing non-POD classes through ellipsis (...).
+# define GTEST_IS_NULL_LITERAL_(x) false
+#else
+# define GTEST_IS_NULL_LITERAL_(x) \
+ (sizeof(::testing::internal::IsNullLiteralHelper(x)) == 1)
+#endif // GTEST_ELLIPSIS_NEEDS_POD_
+
+// Appends the user-supplied message to the Google-Test-generated message.
+GTEST_API_ std::string AppendUserMessage(
+ const std::string& gtest_msg, const Message& user_msg);
+
+#if GTEST_HAS_EXCEPTIONS
+
+// This exception is thrown by (and only by) a failed Google Test
+// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions
+// are enabled). We derive it from std::runtime_error, which is for
+// errors presumably detectable only at run time. Since
+// std::runtime_error inherits from std::exception, many testing
+// frameworks know how to extract and print the message inside it.
+class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error {
+ public:
+ explicit GoogleTestFailureException(const TestPartResult& failure);
+};
+
+#endif // GTEST_HAS_EXCEPTIONS
+
+// A helper class for creating scoped traces in user programs.
+class GTEST_API_ ScopedTrace {
+ public:
+ // The c'tor pushes the given source file location and message onto
+ // a trace stack maintained by Google Test.
+ ScopedTrace(const char* file, int line, const Message& message);
+
+ // The d'tor pops the info pushed by the c'tor.
+ //
+ // Note that the d'tor is not virtual in order to be efficient.
+ // Don't inherit from ScopedTrace!
+ ~ScopedTrace();
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace);
+} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its
+ // c'tor and d'tor. Therefore it doesn't
+ // need to be used otherwise.
+
+// Constructs and returns the message for an equality assertion
+// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure.
+//
+// The first four parameters are the expressions used in the assertion
+// and their values, as strings. For example, for ASSERT_EQ(foo, bar)
+// where foo is 5 and bar is 6, we have:
+//
+// expected_expression: "foo"
+// actual_expression: "bar"
+// expected_value: "5"
+// actual_value: "6"
+//
+// The ignoring_case parameter is true iff the assertion is a
+// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will
+// be inserted into the message.
+GTEST_API_ AssertionResult EqFailure(const char* expected_expression,
+ const char* actual_expression,
+ const std::string& expected_value,
+ const std::string& actual_value,
+ bool ignoring_case);
+
+// Constructs a failure message for Boolean assertions such as EXPECT_TRUE.
+GTEST_API_ std::string GetBoolAssertionFailureMessage(
+ const AssertionResult& assertion_result,
+ const char* expression_text,
+ const char* actual_predicate_value,
+ const char* expected_predicate_value);
+
+// This template class represents an IEEE floating-point number
+// (either single-precision or double-precision, depending on the
+// template parameters).
+//
+// The purpose of this class is to do more sophisticated number
+// comparison. (Due to round-off error, etc, it's very unlikely that
+// two floating-points will be equal exactly. Hence a naive
+// comparison by the == operation often doesn't work.)
+//
+// Format of IEEE floating-point:
+//
+// The most-significant bit being the leftmost, an IEEE
+// floating-point looks like
+//
+// sign_bit exponent_bits fraction_bits
+//
+// Here, sign_bit is a single bit that designates the sign of the
+// number.
+//
+// For float, there are 8 exponent bits and 23 fraction bits.
+//
+// For double, there are 11 exponent bits and 52 fraction bits.
+//
+// More details can be found at
+// http://en.wikipedia.org/wiki/IEEE_floating-point_standard.
+//
+// Template parameter:
+//
+// RawType: the raw floating-point type (either float or double)
+template <typename RawType>
+class FloatingPoint {
+ public:
+ // Defines the unsigned integer type that has the same size as the
+ // floating point number.
+ typedef typename TypeWithSize<sizeof(RawType)>::UInt Bits;
+
+ // Constants.
+
+ // # of bits in a number.
+ static const size_t kBitCount = 8*sizeof(RawType);
+
+ // # of fraction bits in a number.
+ static const size_t kFractionBitCount =
+ std::numeric_limits<RawType>::digits - 1;
+
+ // # of exponent bits in a number.
+ static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount;
+
+ // The mask for the sign bit.
+ static const Bits kSignBitMask = static_cast<Bits>(1) << (kBitCount - 1);
+
+ // The mask for the fraction bits.
+ static const Bits kFractionBitMask =
+ ~static_cast<Bits>(0) >> (kExponentBitCount + 1);
+
+ // The mask for the exponent bits.
+ static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask);
+
+ // How many ULP's (Units in the Last Place) we want to tolerate when
+ // comparing two numbers. The larger the value, the more error we
+ // allow. A 0 value means that two numbers must be exactly the same
+ // to be considered equal.
+ //
+ // The maximum error of a single floating-point operation is 0.5
+ // units in the last place. On Intel CPU's, all floating-point
+ // calculations are done with 80-bit precision, while double has 64
+ // bits. Therefore, 4 should be enough for ordinary use.
+ //
+ // See the following article for more details on ULP:
+ // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
+ static const size_t kMaxUlps = 4;
+
+ // Constructs a FloatingPoint from a raw floating-point number.
+ //
+ // On an Intel CPU, passing a non-normalized NAN (Not a Number)
+ // around may change its bits, although the new value is guaranteed
+ // to be also a NAN. Therefore, don't expect this constructor to
+ // preserve the bits in x when x is a NAN.
+ explicit FloatingPoint(const RawType& x) { u_.value_ = x; }
+
+ // Static methods
+
+ // Reinterprets a bit pattern as a floating-point number.
+ //
+ // This function is needed to test the AlmostEquals() method.
+ static RawType ReinterpretBits(const Bits bits) {
+ FloatingPoint fp(0);
+ fp.u_.bits_ = bits;
+ return fp.u_.value_;
+ }
+
+ // Returns the floating-point number that represent positive infinity.
+ static RawType Infinity() {
+ return ReinterpretBits(kExponentBitMask);
+ }
+
+ // Returns the maximum representable finite floating-point number.
+ static RawType Max();
+
+ // Non-static methods
+
+ // Returns the bits that represents this number.
+ const Bits &bits() const { return u_.bits_; }
+
+ // Returns the exponent bits of this number.
+ Bits exponent_bits() const { return kExponentBitMask & u_.bits_; }
+
+ // Returns the fraction bits of this number.
+ Bits fraction_bits() const { return kFractionBitMask & u_.bits_; }
+
+ // Returns the sign bit of this number.
+ Bits sign_bit() const { return kSignBitMask & u_.bits_; }
+
+ // Returns true iff this is NAN (not a number).
+ bool is_nan() const {
+ // It's a NAN if the exponent bits are all ones and the fraction
+ // bits are not entirely zeros.
+ return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0);
+ }
+
+ // Returns true iff this number is at most kMaxUlps ULP's away from
+ // rhs. In particular, this function:
+ //
+ // - returns false if either number is (or both are) NAN.
+ // - treats really large numbers as almost equal to infinity.
+ // - thinks +0.0 and -0.0 are 0 DLP's apart.
+ bool AlmostEquals(const FloatingPoint& rhs) const {
+ // The IEEE standard says that any comparison operation involving
+ // a NAN must return false.
+ if (is_nan() || rhs.is_nan()) return false;
+
+ return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_)
+ <= kMaxUlps;
+ }
+
+ private:
+ // The data type used to store the actual floating-point number.
+ union FloatingPointUnion {
+ RawType value_; // The raw floating-point number.
+ Bits bits_; // The bits that represent the number.
+ };
+
+ // Converts an integer from the sign-and-magnitude representation to
+ // the biased representation. More precisely, let N be 2 to the
+ // power of (kBitCount - 1), an integer x is represented by the
+ // unsigned number x + N.
+ //
+ // For instance,
+ //
+ // -N + 1 (the most negative number representable using
+ // sign-and-magnitude) is represented by 1;
+ // 0 is represented by N; and
+ // N - 1 (the biggest number representable using
+ // sign-and-magnitude) is represented by 2N - 1.
+ //
+ // Read http://en.wikipedia.org/wiki/Signed_number_representations
+ // for more details on signed number representations.
+ static Bits SignAndMagnitudeToBiased(const Bits &sam) {
+ if (kSignBitMask & sam) {
+ // sam represents a negative number.
+ return ~sam + 1;
+ } else {
+ // sam represents a positive number.
+ return kSignBitMask | sam;
+ }
+ }
+
+ // Given two numbers in the sign-and-magnitude representation,
+ // returns the distance between them as an unsigned number.
+ static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1,
+ const Bits &sam2) {
+ const Bits biased1 = SignAndMagnitudeToBiased(sam1);
+ const Bits biased2 = SignAndMagnitudeToBiased(sam2);
+ return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1);
+ }
+
+ FloatingPointUnion u_;
+};
+
+// We cannot use std::numeric_limits<T>::max() as it clashes with the max()
+// macro defined by <windows.h>.
+template <>
+inline float FloatingPoint<float>::Max() { return FLT_MAX; }
+template <>
+inline double FloatingPoint<double>::Max() { return DBL_MAX; }
+
+// Typedefs the instances of the FloatingPoint template class that we
+// care to use.
+typedef FloatingPoint<float> Float;
+typedef FloatingPoint<double> Double;
+
+// In order to catch the mistake of putting tests that use different
+// test fixture classes in the same test case, we need to assign
+// unique IDs to fixture classes and compare them. The TypeId type is
+// used to hold such IDs. The user should treat TypeId as an opaque
+// type: the only operation allowed on TypeId values is to compare
+// them for equality using the == operator.
+typedef const void* TypeId;
+
+template <typename T>
+class TypeIdHelper {
+ public:
+ // dummy_ must not have a const type. Otherwise an overly eager
+ // compiler (e.g. MSVC 7.1 & 8.0) may try to merge
+ // TypeIdHelper<T>::dummy_ for different Ts as an "optimization".
+ static bool dummy_;
+};
+
+template <typename T>
+bool TypeIdHelper<T>::dummy_ = false;
+
+// GetTypeId<T>() returns the ID of type T. Different values will be
+// returned for different types. Calling the function twice with the
+// same type argument is guaranteed to return the same ID.
+template <typename T>
+TypeId GetTypeId() {
+ // The compiler is required to allocate a different
+ // TypeIdHelper<T>::dummy_ variable for each T used to instantiate
+ // the template. Therefore, the address of dummy_ is guaranteed to
+ // be unique.
+ return &(TypeIdHelper<T>::dummy_);
+}
+
+// Returns the type ID of ::testing::Test. Always call this instead
+// of GetTypeId< ::testing::Test>() to get the type ID of
+// ::testing::Test, as the latter may give the wrong result due to a
+// suspected linker bug when compiling Google Test as a Mac OS X
+// framework.
+GTEST_API_ TypeId GetTestTypeId();
+
+// Defines the abstract factory interface that creates instances
+// of a Test object.
+class TestFactoryBase {
+ public:
+ virtual ~TestFactoryBase() {}
+
+ // Creates a test instance to run. The instance is both created and destroyed
+ // within TestInfoImpl::Run()
+ virtual Test* CreateTest() = 0;
+
+ protected:
+ TestFactoryBase() {}
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestFactoryBase);
+};
+
+// This class provides implementation of TeastFactoryBase interface.
+// It is used in TEST and TEST_F macros.
+template <class TestClass>
+class TestFactoryImpl : public TestFactoryBase {
+ public:
+ virtual Test* CreateTest() { return new TestClass; }
+};
+
+#if GTEST_OS_WINDOWS
+
+// Predicate-formatters for implementing the HRESULT checking macros
+// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}
+// We pass a long instead of HRESULT to avoid causing an
+// include dependency for the HRESULT type.
+GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr,
+ long hr); // NOLINT
+GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr,
+ long hr); // NOLINT
+
+#endif // GTEST_OS_WINDOWS
+
+// Types of SetUpTestCase() and TearDownTestCase() functions.
+typedef void (*SetUpTestCaseFunc)();
+typedef void (*TearDownTestCaseFunc)();
+
+// Creates a new TestInfo object and registers it with Google Test;
+// returns the created object.
+//
+// Arguments:
+//
+// test_case_name: name of the test case
+// name: name of the test
+// type_param the name of the test's type parameter, or NULL if
+// this is not a typed or a type-parameterized test.
+// value_param text representation of the test's value parameter,
+// or NULL if this is not a type-parameterized test.
+// fixture_class_id: ID of the test fixture class
+// set_up_tc: pointer to the function that sets up the test case
+// tear_down_tc: pointer to the function that tears down the test case
+// factory: pointer to the factory that creates a test object.
+// The newly created TestInfo instance will assume
+// ownership of the factory object.
+GTEST_API_ TestInfo* MakeAndRegisterTestInfo(
+ const char* test_case_name,
+ const char* name,
+ const char* type_param,
+ const char* value_param,
+ TypeId fixture_class_id,
+ SetUpTestCaseFunc set_up_tc,
+ TearDownTestCaseFunc tear_down_tc,
+ TestFactoryBase* factory);
+
+// If *pstr starts with the given prefix, modifies *pstr to be right
+// past the prefix and returns true; otherwise leaves *pstr unchanged
+// and returns false. None of pstr, *pstr, and prefix can be NULL.
+GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr);
+
+#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
+
+// State of the definition of a type-parameterized test case.
+class GTEST_API_ TypedTestCasePState {
+ public:
+ TypedTestCasePState() : registered_(false) {}
+
+ // Adds the given test name to defined_test_names_ and return true
+ // if the test case hasn't been registered; otherwise aborts the
+ // program.
+ bool AddTestName(const char* file, int line, const char* case_name,
+ const char* test_name) {
+ if (registered_) {
+ fprintf(stderr, "%s Test %s must be defined before "
+ "REGISTER_TYPED_TEST_CASE_P(%s, ...).\n",
+ FormatFileLocation(file, line).c_str(), test_name, case_name);
+ fflush(stderr);
+ posix::Abort();
+ }
+ defined_test_names_.insert(test_name);
+ return true;
+ }
+
+ // Verifies that registered_tests match the test names in
+ // defined_test_names_; returns registered_tests if successful, or
+ // aborts the program otherwise.
+ const char* VerifyRegisteredTestNames(
+ const char* file, int line, const char* registered_tests);
+
+ private:
+ bool registered_;
+ ::std::set<const char*> defined_test_names_;
+};
+
+// Skips to the first non-space char after the first comma in 'str';
+// returns NULL if no comma is found in 'str'.
+inline const char* SkipComma(const char* str) {
+ const char* comma = strchr(str, ',');
+ if (comma == NULL) {
+ return NULL;
+ }
+ while (IsSpace(*(++comma))) {}
+ return comma;
+}
+
+// Returns the prefix of 'str' before the first comma in it; returns
+// the entire string if it contains no comma.
+inline std::string GetPrefixUntilComma(const char* str) {
+ const char* comma = strchr(str, ',');
+ return comma == NULL ? str : std::string(str, comma);
+}
+
+// TypeParameterizedTest<Fixture, TestSel, Types>::Register()
+// registers a list of type-parameterized tests with Google Test. The
+// return value is insignificant - we just need to return something
+// such that we can call this function in a namespace scope.
+//
+// Implementation note: The GTEST_TEMPLATE_ macro declares a template
+// template parameter. It's defined in gtest-type-util.h.
+template <GTEST_TEMPLATE_ Fixture, class TestSel, typename Types>
+class TypeParameterizedTest {
+ public:
+ // 'index' is the index of the test in the type list 'Types'
+ // specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase,
+ // Types). Valid values for 'index' are [0, N - 1] where N is the
+ // length of Types.
+ static bool Register(const char* prefix, const char* case_name,
+ const char* test_names, int index) {
+ typedef typename Types::Head Type;
+ typedef Fixture<Type> FixtureClass;
+ typedef typename GTEST_BIND_(TestSel, Type) TestClass;
+
+ // First, registers the first type-parameterized test in the type
+ // list.
+ MakeAndRegisterTestInfo(
+ (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + "/"
+ + StreamableToString(index)).c_str(),
+ GetPrefixUntilComma(test_names).c_str(),
+ GetTypeName<Type>().c_str(),
+ NULL, // No value parameter.
+ GetTypeId<FixtureClass>(),
+ TestClass::SetUpTestCase,
+ TestClass::TearDownTestCase,
+ new TestFactoryImpl<TestClass>);
+
+ // Next, recurses (at compile time) with the tail of the type list.
+ return TypeParameterizedTest<Fixture, TestSel, typename Types::Tail>
+ ::Register(prefix, case_name, test_names, index + 1);
+ }
+};
+
+// The base case for the compile time recursion.
+template <GTEST_TEMPLATE_ Fixture, class TestSel>
+class TypeParameterizedTest<Fixture, TestSel, Types0> {
+ public:
+ static bool Register(const char* /*prefix*/, const char* /*case_name*/,
+ const char* /*test_names*/, int /*index*/) {
+ return true;
+ }
+};
+
+// TypeParameterizedTestCase<Fixture, Tests, Types>::Register()
+// registers *all combinations* of 'Tests' and 'Types' with Google
+// Test. The return value is insignificant - we just need to return
+// something such that we can call this function in a namespace scope.
+template <GTEST_TEMPLATE_ Fixture, typename Tests, typename Types>
+class TypeParameterizedTestCase {
+ public:
+ static bool Register(const char* prefix, const char* case_name,
+ const char* test_names) {
+ typedef typename Tests::Head Head;
+
+ // First, register the first test in 'Test' for each type in 'Types'.
+ TypeParameterizedTest<Fixture, Head, Types>::Register(
+ prefix, case_name, test_names, 0);
+
+ // Next, recurses (at compile time) with the tail of the test list.
+ return TypeParameterizedTestCase<Fixture, typename Tests::Tail, Types>
+ ::Register(prefix, case_name, SkipComma(test_names));
+ }
+};
+
+// The base case for the compile time recursion.
+template <GTEST_TEMPLATE_ Fixture, typename Types>
+class TypeParameterizedTestCase<Fixture, Templates0, Types> {
+ public:
+ static bool Register(const char* /*prefix*/, const char* /*case_name*/,
+ const char* /*test_names*/) {
+ return true;
+ }
+};
+
+#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
+
+// Returns the current OS stack trace as an std::string.
+//
+// The maximum number of stack frames to be included is specified by
+// the gtest_stack_trace_depth flag. The skip_count parameter
+// specifies the number of top frames to be skipped, which doesn't
+// count against the number of frames to be included.
+//
+// For example, if Foo() calls Bar(), which in turn calls
+// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in
+// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't.
+GTEST_API_ std::string GetCurrentOsStackTraceExceptTop(
+ UnitTest* unit_test, int skip_count);
+
+// Helpers for suppressing warnings on unreachable code or constant
+// condition.
+
+// Always returns true.
+GTEST_API_ bool AlwaysTrue();
+
+// Always returns false.
+inline bool AlwaysFalse() { return !AlwaysTrue(); }
+
+// Helper for suppressing false warning from Clang on a const char*
+// variable declared in a conditional expression always being NULL in
+// the else branch.
+struct GTEST_API_ ConstCharPtr {
+ ConstCharPtr(const char* str) : value(str) {}
+ operator bool() const { return true; }
+ const char* value;
+};
+
+// A simple Linear Congruential Generator for generating random
+// numbers with a uniform distribution. Unlike rand() and srand(), it
+// doesn't use global state (and therefore can't interfere with user
+// code). Unlike rand_r(), it's portable. An LCG isn't very random,
+// but it's good enough for our purposes.
+class GTEST_API_ Random {
+ public:
+ static const UInt32 kMaxRange = 1u << 31;
+
+ explicit Random(UInt32 seed) : state_(seed) {}
+
+ void Reseed(UInt32 seed) { state_ = seed; }
+
+ // Generates a random number from [0, range). Crashes if 'range' is
+ // 0 or greater than kMaxRange.
+ UInt32 Generate(UInt32 range);
+
+ private:
+ UInt32 state_;
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(Random);
+};
+
+// Defining a variable of type CompileAssertTypesEqual<T1, T2> will cause a
+// compiler error iff T1 and T2 are different types.
+template <typename T1, typename T2>
+struct CompileAssertTypesEqual;
+
+template <typename T>
+struct CompileAssertTypesEqual<T, T> {
+};
+
+// Removes the reference from a type if it is a reference type,
+// otherwise leaves it unchanged. This is the same as
+// tr1::remove_reference, which is not widely available yet.
+template <typename T>
+struct RemoveReference { typedef T type; }; // NOLINT
+template <typename T>
+struct RemoveReference<T&> { typedef T type; }; // NOLINT
+
+// A handy wrapper around RemoveReference that works when the argument
+// T depends on template parameters.
+#define GTEST_REMOVE_REFERENCE_(T) \
+ typename ::testing::internal::RemoveReference<T>::type
+
+// Removes const from a type if it is a const type, otherwise leaves
+// it unchanged. This is the same as tr1::remove_const, which is not
+// widely available yet.
+template <typename T>
+struct RemoveConst { typedef T type; }; // NOLINT
+template <typename T>
+struct RemoveConst<const T> { typedef T type; }; // NOLINT
+
+// MSVC 8.0, Sun C++, and IBM XL C++ have a bug which causes the above
+// definition to fail to remove the const in 'const int[3]' and 'const
+// char[3][4]'. The following specialization works around the bug.
+template <typename T, size_t N>
+struct RemoveConst<const T[N]> {
+ typedef typename RemoveConst<T>::type type[N];
+};
+
+#if defined(_MSC_VER) && _MSC_VER < 1400
+// This is the only specialization that allows VC++ 7.1 to remove const in
+// 'const int[3] and 'const int[3][4]'. However, it causes trouble with GCC
+// and thus needs to be conditionally compiled.
+template <typename T, size_t N>
+struct RemoveConst<T[N]> {
+ typedef typename RemoveConst<T>::type type[N];
+};
+#endif
+
+// A handy wrapper around RemoveConst that works when the argument
+// T depends on template parameters.
+#define GTEST_REMOVE_CONST_(T) \
+ typename ::testing::internal::RemoveConst<T>::type
+
+// Turns const U&, U&, const U, and U all into U.
+#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \
+ GTEST_REMOVE_CONST_(GTEST_REMOVE_REFERENCE_(T))
+
+// Adds reference to a type if it is not a reference type,
+// otherwise leaves it unchanged. This is the same as
+// tr1::add_reference, which is not widely available yet.
+template <typename T>
+struct AddReference { typedef T& type; }; // NOLINT
+template <typename T>
+struct AddReference<T&> { typedef T& type; }; // NOLINT
+
+// A handy wrapper around AddReference that works when the argument T
+// depends on template parameters.
+#define GTEST_ADD_REFERENCE_(T) \
+ typename ::testing::internal::AddReference<T>::type
+
+// Adds a reference to const on top of T as necessary. For example,
+// it transforms
+//
+// char ==> const char&
+// const char ==> const char&
+// char& ==> const char&
+// const char& ==> const char&
+//
+// The argument T must depend on some template parameters.
+#define GTEST_REFERENCE_TO_CONST_(T) \
+ GTEST_ADD_REFERENCE_(const GTEST_REMOVE_REFERENCE_(T))
+
+// ImplicitlyConvertible<From, To>::value is a compile-time bool
+// constant that's true iff type From can be implicitly converted to
+// type To.
+template <typename From, typename To>
+class ImplicitlyConvertible {
+ private:
+ // We need the following helper functions only for their types.
+ // They have no implementations.
+
+ // MakeFrom() is an expression whose type is From. We cannot simply
+ // use From(), as the type From may not have a public default
+ // constructor.
+ static From MakeFrom();
+
+ // These two functions are overloaded. Given an expression
+ // Helper(x), the compiler will pick the first version if x can be
+ // implicitly converted to type To; otherwise it will pick the
+ // second version.
+ //
+ // The first version returns a value of size 1, and the second
+ // version returns a value of size 2. Therefore, by checking the
+ // size of Helper(x), which can be done at compile time, we can tell
+ // which version of Helper() is used, and hence whether x can be
+ // implicitly converted to type To.
+ static char Helper(To);
+ static char (&Helper(...))[2]; // NOLINT
+
+ // We have to put the 'public' section after the 'private' section,
+ // or MSVC refuses to compile the code.
+ public:
+ // MSVC warns about implicitly converting from double to int for
+ // possible loss of data, so we need to temporarily disable the
+ // warning.
+#ifdef _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4244) // Temporarily disables warning 4244.
+
+ static const bool value =
+ sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1;
+# pragma warning(pop) // Restores the warning state.
+#elif defined(__BORLANDC__)
+ // C++Builder cannot use member overload resolution during template
+ // instantiation. The simplest workaround is to use its C++0x type traits
+ // functions (C++Builder 2009 and above only).
+ static const bool value = __is_convertible(From, To);
+#else
+ static const bool value =
+ sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1;
+#endif // _MSV_VER
+};
+template <typename From, typename To>
+const bool ImplicitlyConvertible<From, To>::value;
+
+// IsAProtocolMessage<T>::value is a compile-time bool constant that's
+// true iff T is type ProtocolMessage, proto2::Message, or a subclass
+// of those.
+template <typename T>
+struct IsAProtocolMessage
+ : public bool_constant<
+ ImplicitlyConvertible<const T*, const ::ProtocolMessage*>::value ||
+ ImplicitlyConvertible<const T*, const ::proto2::Message*>::value> {
+};
+
+// When the compiler sees expression IsContainerTest<C>(0), if C is an
+// STL-style container class, the first overload of IsContainerTest
+// will be viable (since both C::iterator* and C::const_iterator* are
+// valid types and NULL can be implicitly converted to them). It will
+// be picked over the second overload as 'int' is a perfect match for
+// the type of argument 0. If C::iterator or C::const_iterator is not
+// a valid type, the first overload is not viable, and the second
+// overload will be picked. Therefore, we can determine whether C is
+// a container class by checking the type of IsContainerTest<C>(0).
+// The value of the expression is insignificant.
+//
+// Note that we look for both C::iterator and C::const_iterator. The
+// reason is that C++ injects the name of a class as a member of the
+// class itself (e.g. you can refer to class iterator as either
+// 'iterator' or 'iterator::iterator'). If we look for C::iterator
+// only, for example, we would mistakenly think that a class named
+// iterator is an STL container.
+//
+// Also note that the simpler approach of overloading
+// IsContainerTest(typename C::const_iterator*) and
+// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++.
+typedef int IsContainer;
+template <class C>
+IsContainer IsContainerTest(int /* dummy */,
+ typename C::iterator* /* it */ = NULL,
+ typename C::const_iterator* /* const_it */ = NULL) {
+ return 0;
+}
+
+typedef char IsNotContainer;
+template <class C>
+IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; }
+
+// EnableIf<condition>::type is void when 'Cond' is true, and
+// undefined when 'Cond' is false. To use SFINAE to make a function
+// overload only apply when a particular expression is true, add
+// "typename EnableIf<expression>::type* = 0" as the last parameter.
+template<bool> struct EnableIf;
+template<> struct EnableIf<true> { typedef void type; }; // NOLINT
+
+// Utilities for native arrays.
+
+// ArrayEq() compares two k-dimensional native arrays using the
+// elements' operator==, where k can be any integer >= 0. When k is
+// 0, ArrayEq() degenerates into comparing a single pair of values.
+
+template <typename T, typename U>
+bool ArrayEq(const T* lhs, size_t size, const U* rhs);
+
+// This generic version is used when k is 0.
+template <typename T, typename U>
+inline bool ArrayEq(const T& lhs, const U& rhs) { return lhs == rhs; }
+
+// This overload is used when k >= 1.
+template <typename T, typename U, size_t N>
+inline bool ArrayEq(const T(&lhs)[N], const U(&rhs)[N]) {
+ return internal::ArrayEq(lhs, N, rhs);
+}
+
+// This helper reduces code bloat. If we instead put its logic inside
+// the previous ArrayEq() function, arrays with different sizes would
+// lead to different copies of the template code.
+template <typename T, typename U>
+bool ArrayEq(const T* lhs, size_t size, const U* rhs) {
+ for (size_t i = 0; i != size; i++) {
+ if (!internal::ArrayEq(lhs[i], rhs[i]))
+ return false;
+ }
+ return true;
+}
+
+// Finds the first element in the iterator range [begin, end) that
+// equals elem. Element may be a native array type itself.
+template <typename Iter, typename Element>
+Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) {
+ for (Iter it = begin; it != end; ++it) {
+ if (internal::ArrayEq(*it, elem))
+ return it;
+ }
+ return end;
+}
+
+// CopyArray() copies a k-dimensional native array using the elements'
+// operator=, where k can be any integer >= 0. When k is 0,
+// CopyArray() degenerates into copying a single value.
+
+template <typename T, typename U>
+void CopyArray(const T* from, size_t size, U* to);
+
+// This generic version is used when k is 0.
+template <typename T, typename U>
+inline void CopyArray(const T& from, U* to) { *to = from; }
+
+// This overload is used when k >= 1.
+template <typename T, typename U, size_t N>
+inline void CopyArray(const T(&from)[N], U(*to)[N]) {
+ internal::CopyArray(from, N, *to);
+}
+
+// This helper reduces code bloat. If we instead put its logic inside
+// the previous CopyArray() function, arrays with different sizes
+// would lead to different copies of the template code.
+template <typename T, typename U>
+void CopyArray(const T* from, size_t size, U* to) {
+ for (size_t i = 0; i != size; i++) {
+ internal::CopyArray(from[i], to + i);
+ }
+}
+
+// The relation between an NativeArray object (see below) and the
+// native array it represents.
+enum RelationToSource {
+ kReference, // The NativeArray references the native array.
+ kCopy // The NativeArray makes a copy of the native array and
+ // owns the copy.
+};
+
+// Adapts a native array to a read-only STL-style container. Instead
+// of the complete STL container concept, this adaptor only implements
+// members useful for Google Mock's container matchers. New members
+// should be added as needed. To simplify the implementation, we only
+// support Element being a raw type (i.e. having no top-level const or
+// reference modifier). It's the client's responsibility to satisfy
+// this requirement. Element can be an array type itself (hence
+// multi-dimensional arrays are supported).
+template <typename Element>
+class NativeArray {
+ public:
+ // STL-style container typedefs.
+ typedef Element value_type;
+ typedef Element* iterator;
+ typedef const Element* const_iterator;
+
+ // Constructs from a native array.
+ NativeArray(const Element* array, size_t count, RelationToSource relation) {
+ Init(array, count, relation);
+ }
+
+ // Copy constructor.
+ NativeArray(const NativeArray& rhs) {
+ Init(rhs.array_, rhs.size_, rhs.relation_to_source_);
+ }
+
+ ~NativeArray() {
+ // Ensures that the user doesn't instantiate NativeArray with a
+ // const or reference type.
+ static_cast<void>(StaticAssertTypeEqHelper<Element,
+ GTEST_REMOVE_REFERENCE_AND_CONST_(Element)>());
+ if (relation_to_source_ == kCopy)
+ delete[] array_;
+ }
+
+ // STL-style container methods.
+ size_t size() const { return size_; }
+ const_iterator begin() const { return array_; }
+ const_iterator end() const { return array_ + size_; }
+ bool operator==(const NativeArray& rhs) const {
+ return size() == rhs.size() &&
+ ArrayEq(begin(), size(), rhs.begin());
+ }
+
+ private:
+ // Initializes this object; makes a copy of the input array if
+ // 'relation' is kCopy.
+ void Init(const Element* array, size_t a_size, RelationToSource relation) {
+ if (relation == kReference) {
+ array_ = array;
+ } else {
+ Element* const copy = new Element[a_size];
+ CopyArray(array, a_size, copy);
+ array_ = copy;
+ }
+ size_ = a_size;
+ relation_to_source_ = relation;
+ }
+
+ const Element* array_;
+ size_t size_;
+ RelationToSource relation_to_source_;
+
+ GTEST_DISALLOW_ASSIGN_(NativeArray);
+};
+
+} // namespace internal
+} // namespace testing
+
+#define GTEST_MESSAGE_AT_(file, line, message, result_type) \
+ ::testing::internal::AssertHelper(result_type, file, line, message) \
+ = ::testing::Message()
+
+#define GTEST_MESSAGE_(message, result_type) \
+ GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type)
+
+#define GTEST_FATAL_FAILURE_(message) \
+ return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure)
+
+#define GTEST_NONFATAL_FAILURE_(message) \
+ GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure)
+
+#define GTEST_SUCCESS_(message) \
+ GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess)
+
+// Suppresses MSVC warnings 4072 (unreachable code) for the code following
+// statement if it returns or throws (or doesn't return or throw in some
+// situations).
+#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \
+ if (::testing::internal::AlwaysTrue()) { statement; }
+
+#define GTEST_TEST_THROW_(statement, expected_exception, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::ConstCharPtr gtest_msg = "") { \
+ bool gtest_caught_expected = false; \
+ try { \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ } \
+ catch (expected_exception const&) { \
+ gtest_caught_expected = true; \
+ } \
+ catch (...) { \
+ gtest_msg.value = \
+ "Expected: " #statement " throws an exception of type " \
+ #expected_exception ".\n Actual: it throws a different type."; \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
+ } \
+ if (!gtest_caught_expected) { \
+ gtest_msg.value = \
+ "Expected: " #statement " throws an exception of type " \
+ #expected_exception ".\n Actual: it throws nothing."; \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
+ } \
+ } else \
+ GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \
+ fail(gtest_msg.value)
+
+#define GTEST_TEST_NO_THROW_(statement, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::AlwaysTrue()) { \
+ try { \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ } \
+ catch (...) { \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \
+ } \
+ } else \
+ GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \
+ fail("Expected: " #statement " doesn't throw an exception.\n" \
+ " Actual: it throws.")
+
+#define GTEST_TEST_ANY_THROW_(statement, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::AlwaysTrue()) { \
+ bool gtest_caught_any = false; \
+ try { \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ } \
+ catch (...) { \
+ gtest_caught_any = true; \
+ } \
+ if (!gtest_caught_any) { \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \
+ } \
+ } else \
+ GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__): \
+ fail("Expected: " #statement " throws an exception.\n" \
+ " Actual: it doesn't.")
+
+
+// Implements Boolean test assertions such as EXPECT_TRUE. expression can be
+// either a boolean expression or an AssertionResult. text is a textual
+// represenation of expression as it was passed into the EXPECT_TRUE.
+#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (const ::testing::AssertionResult gtest_ar_ = \
+ ::testing::AssertionResult(expression)) \
+ ; \
+ else \
+ fail(::testing::internal::GetBoolAssertionFailureMessage(\
+ gtest_ar_, text, #actual, #expected).c_str())
+
+#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::AlwaysTrue()) { \
+ ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \
+ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+ if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \
+ goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \
+ } \
+ } else \
+ GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__): \
+ fail("Expected: " #statement " doesn't generate new fatal " \
+ "failures in the current thread.\n" \
+ " Actual: it does.")
+
+// Expands to the name of the class that implements the given test.
+#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
+ test_case_name##_##test_name##_Test
+
+// Helper macro for defining tests.
+#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\
+class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\
+ public:\
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\
+ private:\
+ virtual void TestBody();\
+ static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;\
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(\
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\
+};\
+\
+::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\
+ ::test_info_ =\
+ ::testing::internal::MakeAndRegisterTestInfo(\
+ #test_case_name, #test_name, NULL, NULL, \
+ (parent_id), \
+ parent_class::SetUpTestCase, \
+ parent_class::TearDownTestCase, \
+ new ::testing::internal::TestFactoryImpl<\
+ GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\
+void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-linked_ptr.h b/extern/gtest/include/gtest/internal/gtest-linked_ptr.h
new file mode 100644
index 00000000000..b1362cd002c
--- /dev/null
+++ b/extern/gtest/include/gtest/internal/gtest-linked_ptr.h
@@ -0,0 +1,233 @@
+// Copyright 2003 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: Dan Egnor (egnor@google.com)
+//
+// A "smart" pointer type with reference tracking. Every pointer to a
+// particular object is kept on a circular linked list. When the last pointer
+// to an object is destroyed or reassigned, the object is deleted.
+//
+// Used properly, this deletes the object when the last reference goes away.
+// There are several caveats:
+// - Like all reference counting schemes, cycles lead to leaks.
+// - Each smart pointer is actually two pointers (8 bytes instead of 4).
+// - Every time a pointer is assigned, the entire list of pointers to that
+// object is traversed. This class is therefore NOT SUITABLE when there
+// will often be more than two or three pointers to a particular object.
+// - References are only tracked as long as linked_ptr<> objects are copied.
+// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS
+// will happen (double deletion).
+//
+// A good use of this class is storing object references in STL containers.
+// You can safely put linked_ptr<> in a vector<>.
+// Other uses may not be as good.
+//
+// Note: If you use an incomplete type with linked_ptr<>, the class
+// *containing* linked_ptr<> must have a constructor and destructor (even
+// if they do nothing!).
+//
+// Bill Gibbons suggested we use something like this.
+//
+// Thread Safety:
+// Unlike other linked_ptr implementations, in this implementation
+// a linked_ptr object is thread-safe in the sense that:
+// - it's safe to copy linked_ptr objects concurrently,
+// - it's safe to copy *from* a linked_ptr and read its underlying
+// raw pointer (e.g. via get()) concurrently, and
+// - it's safe to write to two linked_ptrs that point to the same
+// shared object concurrently.
+// TODO(wan@google.com): rename this to safe_linked_ptr to avoid
+// confusion with normal linked_ptr.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "gtest/internal/gtest-port.h"
+
+namespace testing {
+namespace internal {
+
+// Protects copying of all linked_ptr objects.
+GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_linked_ptr_mutex);
+
+// This is used internally by all instances of linked_ptr<>. It needs to be
+// a non-template class because different types of linked_ptr<> can refer to
+// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)).
+// So, it needs to be possible for different types of linked_ptr to participate
+// in the same circular linked list, so we need a single class type here.
+//
+// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr<T>.
+class linked_ptr_internal {
+ public:
+ // Create a new circle that includes only this instance.
+ void join_new() {
+ next_ = this;
+ }
+
+ // Many linked_ptr operations may change p.link_ for some linked_ptr
+ // variable p in the same circle as this object. Therefore we need
+ // to prevent two such operations from occurring concurrently.
+ //
+ // Note that different types of linked_ptr objects can coexist in a
+ // circle (e.g. linked_ptr<Base>, linked_ptr<Derived1>, and
+ // linked_ptr<Derived2>). Therefore we must use a single mutex to
+ // protect all linked_ptr objects. This can create serious
+ // contention in production code, but is acceptable in a testing
+ // framework.
+
+ // Join an existing circle.
+ void join(linked_ptr_internal const* ptr)
+ GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) {
+ MutexLock lock(&g_linked_ptr_mutex);
+
+ linked_ptr_internal const* p = ptr;
+ while (p->next_ != ptr) p = p->next_;
+ p->next_ = this;
+ next_ = ptr;
+ }
+
+ // Leave whatever circle we're part of. Returns true if we were the
+ // last member of the circle. Once this is done, you can join() another.
+ bool depart()
+ GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) {
+ MutexLock lock(&g_linked_ptr_mutex);
+
+ if (next_ == this) return true;
+ linked_ptr_internal const* p = next_;
+ while (p->next_ != this) p = p->next_;
+ p->next_ = next_;
+ return false;
+ }
+
+ private:
+ mutable linked_ptr_internal const* next_;
+};
+
+template <typename T>
+class linked_ptr {
+ public:
+ typedef T element_type;
+
+ // Take over ownership of a raw pointer. This should happen as soon as
+ // possible after the object is created.
+ explicit linked_ptr(T* ptr = NULL) { capture(ptr); }
+ ~linked_ptr() { depart(); }
+
+ // Copy an existing linked_ptr<>, adding ourselves to the list of references.
+ template <typename U> linked_ptr(linked_ptr<U> const& ptr) { copy(&ptr); }
+ linked_ptr(linked_ptr const& ptr) { // NOLINT
+ assert(&ptr != this);
+ copy(&ptr);
+ }
+
+ // Assignment releases the old value and acquires the new.
+ template <typename U> linked_ptr& operator=(linked_ptr<U> const& ptr) {
+ depart();
+ copy(&ptr);
+ return *this;
+ }
+
+ linked_ptr& operator=(linked_ptr const& ptr) {
+ if (&ptr != this) {
+ depart();
+ copy(&ptr);
+ }
+ return *this;
+ }
+
+ // Smart pointer members.
+ void reset(T* ptr = NULL) {
+ depart();
+ capture(ptr);
+ }
+ T* get() const { return value_; }
+ T* operator->() const { return value_; }
+ T& operator*() const { return *value_; }
+
+ bool operator==(T* p) const { return value_ == p; }
+ bool operator!=(T* p) const { return value_ != p; }
+ template <typename U>
+ bool operator==(linked_ptr<U> const& ptr) const {
+ return value_ == ptr.get();
+ }
+ template <typename U>
+ bool operator!=(linked_ptr<U> const& ptr) const {
+ return value_ != ptr.get();
+ }
+
+ private:
+ template <typename U>
+ friend class linked_ptr;
+
+ T* value_;
+ linked_ptr_internal link_;
+
+ void depart() {
+ if (link_.depart()) delete value_;
+ }
+
+ void capture(T* ptr) {
+ value_ = ptr;
+ link_.join_new();
+ }
+
+ template <typename U> void copy(linked_ptr<U> const* ptr) {
+ value_ = ptr->get();
+ if (value_)
+ link_.join(&ptr->link_);
+ else
+ link_.join_new();
+ }
+};
+
+template<typename T> inline
+bool operator==(T* ptr, const linked_ptr<T>& x) {
+ return ptr == x.get();
+}
+
+template<typename T> inline
+bool operator!=(T* ptr, const linked_ptr<T>& x) {
+ return ptr != x.get();
+}
+
+// A function to convert T* into linked_ptr<T>
+// Doing e.g. make_linked_ptr(new FooBarBaz<type>(arg)) is a shorter notation
+// for linked_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg))
+template <typename T>
+linked_ptr<T> make_linked_ptr(T* ptr) {
+ return linked_ptr<T>(ptr);
+}
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-param-util-generated.h b/extern/gtest/include/gtest/internal/gtest-param-util-generated.h
new file mode 100644
index 00000000000..e80548592c7
--- /dev/null
+++ b/extern/gtest/include/gtest/internal/gtest-param-util-generated.h
@@ -0,0 +1,5143 @@
+// This file was GENERATED by command:
+// pump.py gtest-param-util-generated.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: vladl@google.com (Vlad Losev)
+
+// Type and function utilities for implementing parameterized tests.
+// This file is generated by a SCRIPT. DO NOT EDIT BY HAND!
+//
+// Currently Google Test supports at most 50 arguments in Values,
+// and at most 10 arguments in Combine. Please contact
+// googletestframework@googlegroups.com if you need more.
+// Please note that the number of arguments to Combine is limited
+// by the maximum arity of the implementation of tr1::tuple which is
+// currently set at 10.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
+
+// scripts/fuse_gtest.py depends on gtest's own header being #included
+// *unconditionally*. Therefore these #includes cannot be moved
+// inside #if GTEST_HAS_PARAM_TEST.
+#include "gtest/internal/gtest-param-util.h"
+#include "gtest/internal/gtest-port.h"
+
+#if GTEST_HAS_PARAM_TEST
+
+namespace testing {
+
+// Forward declarations of ValuesIn(), which is implemented in
+// include/gtest/gtest-param-test.h.
+template <typename ForwardIterator>
+internal::ParamGenerator<
+ typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type>
+ValuesIn(ForwardIterator begin, ForwardIterator end);
+
+template <typename T, size_t N>
+internal::ParamGenerator<T> ValuesIn(const T (&array)[N]);
+
+template <class Container>
+internal::ParamGenerator<typename Container::value_type> ValuesIn(
+ const Container& container);
+
+namespace internal {
+
+// Used in the Values() function to provide polymorphic capabilities.
+template <typename T1>
+class ValueArray1 {
+ public:
+ explicit ValueArray1(T1 v1) : v1_(v1) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const { return ValuesIn(&v1_, &v1_ + 1); }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray1& other);
+
+ const T1 v1_;
+};
+
+template <typename T1, typename T2>
+class ValueArray2 {
+ public:
+ ValueArray2(T1 v1, T2 v2) : v1_(v1), v2_(v2) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray2& other);
+
+ const T1 v1_;
+ const T2 v2_;
+};
+
+template <typename T1, typename T2, typename T3>
+class ValueArray3 {
+ public:
+ ValueArray3(T1 v1, T2 v2, T3 v3) : v1_(v1), v2_(v2), v3_(v3) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray3& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4>
+class ValueArray4 {
+ public:
+ ValueArray4(T1 v1, T2 v2, T3 v3, T4 v4) : v1_(v1), v2_(v2), v3_(v3),
+ v4_(v4) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray4& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+class ValueArray5 {
+ public:
+ ValueArray5(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5) : v1_(v1), v2_(v2), v3_(v3),
+ v4_(v4), v5_(v5) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray5& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+class ValueArray6 {
+ public:
+ ValueArray6(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6) : v1_(v1), v2_(v2),
+ v3_(v3), v4_(v4), v5_(v5), v6_(v6) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray6& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+class ValueArray7 {
+ public:
+ ValueArray7(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7) : v1_(v1),
+ v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray7& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+class ValueArray8 {
+ public:
+ ValueArray8(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+ T8 v8) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray8& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+class ValueArray9 {
+ public:
+ ValueArray9(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8,
+ T9 v9) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray9& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+class ValueArray10 {
+ public:
+ ValueArray10(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray10& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11>
+class ValueArray11 {
+ public:
+ ValueArray11(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
+ v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray11& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12>
+class ValueArray12 {
+ public:
+ ValueArray12(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
+ v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray12& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13>
+class ValueArray13 {
+ public:
+ ValueArray13(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
+ v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
+ v12_(v12), v13_(v13) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray13& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14>
+class ValueArray14 {
+ public:
+ ValueArray14(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) : v1_(v1), v2_(v2), v3_(v3),
+ v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray14& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15>
+class ValueArray15 {
+ public:
+ ValueArray15(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) : v1_(v1), v2_(v2),
+ v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray15& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16>
+class ValueArray16 {
+ public:
+ ValueArray16(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16) : v1_(v1),
+ v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
+ v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
+ v16_(v16) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray16& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17>
+class ValueArray17 {
+ public:
+ ValueArray17(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16,
+ T17 v17) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray17& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18>
+class ValueArray18 {
+ public:
+ ValueArray18(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray18& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19>
+class ValueArray19 {
+ public:
+ ValueArray19(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
+ v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13),
+ v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray19& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20>
+class ValueArray20 {
+ public:
+ ValueArray20(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
+ v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12),
+ v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18),
+ v19_(v19), v20_(v20) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray20& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21>
+class ValueArray21 {
+ public:
+ ValueArray21(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
+ v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
+ v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17),
+ v18_(v18), v19_(v19), v20_(v20), v21_(v21) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray21& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22>
+class ValueArray22 {
+ public:
+ ValueArray22(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22) : v1_(v1), v2_(v2), v3_(v3),
+ v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+ v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray22& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23>
+class ValueArray23 {
+ public:
+ ValueArray23(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23) : v1_(v1), v2_(v2),
+ v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+ v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+ v23_(v23) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray23& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24>
+class ValueArray24 {
+ public:
+ ValueArray24(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24) : v1_(v1),
+ v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
+ v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
+ v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21),
+ v22_(v22), v23_(v23), v24_(v24) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray24& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25>
+class ValueArray25 {
+ public:
+ ValueArray25(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24,
+ T25 v25) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+ v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray25& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26>
+class ValueArray26 {
+ public:
+ ValueArray26(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+ v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray26& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27>
+class ValueArray27 {
+ public:
+ ValueArray27(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
+ v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13),
+ v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19),
+ v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25),
+ v26_(v26), v27_(v27) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray27& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28>
+class ValueArray28 {
+ public:
+ ValueArray28(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
+ v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12),
+ v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18),
+ v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24),
+ v25_(v25), v26_(v26), v27_(v27), v28_(v28) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray28& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29>
+class ValueArray29 {
+ public:
+ ValueArray29(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
+ v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
+ v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17),
+ v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23),
+ v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray29& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30>
+class ValueArray30 {
+ public:
+ ValueArray30(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) : v1_(v1), v2_(v2), v3_(v3),
+ v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+ v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+ v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+ v29_(v29), v30_(v30) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray30& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31>
+class ValueArray31 {
+ public:
+ ValueArray31(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) : v1_(v1), v2_(v2),
+ v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+ v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+ v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+ v29_(v29), v30_(v30), v31_(v31) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray31& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32>
+class ValueArray32 {
+ public:
+ ValueArray32(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32) : v1_(v1),
+ v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
+ v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
+ v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21),
+ v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27),
+ v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray32& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33>
+class ValueArray33 {
+ public:
+ ValueArray33(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32,
+ T33 v33) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+ v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+ v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+ v33_(v33) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray33& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34>
+class ValueArray34 {
+ public:
+ ValueArray34(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+ v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+ v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+ v33_(v33), v34_(v34) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray34& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35>
+class ValueArray35 {
+ public:
+ ValueArray35(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
+ v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13),
+ v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19),
+ v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25),
+ v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31),
+ v32_(v32), v33_(v33), v34_(v34), v35_(v35) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray35& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36>
+class ValueArray36 {
+ public:
+ ValueArray36(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
+ v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12),
+ v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18),
+ v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24),
+ v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30),
+ v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray36& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37>
+class ValueArray37 {
+ public:
+ ValueArray37(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
+ v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
+ v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17),
+ v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23),
+ v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29),
+ v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35),
+ v36_(v36), v37_(v37) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray37& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38>
+class ValueArray38 {
+ public:
+ ValueArray38(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38) : v1_(v1), v2_(v2), v3_(v3),
+ v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+ v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+ v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+ v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34),
+ v35_(v35), v36_(v36), v37_(v37), v38_(v38) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray38& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39>
+class ValueArray39 {
+ public:
+ ValueArray39(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39) : v1_(v1), v2_(v2),
+ v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+ v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+ v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+ v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34),
+ v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray39& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40>
+class ValueArray40 {
+ public:
+ ValueArray40(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) : v1_(v1),
+ v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
+ v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
+ v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21),
+ v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27),
+ v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33),
+ v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39),
+ v40_(v40) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray40& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41>
+class ValueArray41 {
+ public:
+ ValueArray41(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40,
+ T41 v41) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+ v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+ v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+ v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38),
+ v39_(v39), v40_(v40), v41_(v41) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray41& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42>
+class ValueArray42 {
+ public:
+ ValueArray42(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+ v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+ v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+ v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38),
+ v39_(v39), v40_(v40), v41_(v41), v42_(v42) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray42& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43>
+class ValueArray43 {
+ public:
+ ValueArray43(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
+ v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13),
+ v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19),
+ v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25),
+ v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31),
+ v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37),
+ v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_), static_cast<T>(v43_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray43& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+ const T43 v43_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44>
+class ValueArray44 {
+ public:
+ ValueArray44(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43, T44 v44) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
+ v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12),
+ v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18),
+ v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24),
+ v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30),
+ v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36),
+ v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42),
+ v43_(v43), v44_(v44) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray44& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+ const T43 v43_;
+ const T44 v44_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45>
+class ValueArray45 {
+ public:
+ ValueArray45(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43, T44 v44, T45 v45) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
+ v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
+ v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17),
+ v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23),
+ v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29),
+ v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35),
+ v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41),
+ v42_(v42), v43_(v43), v44_(v44), v45_(v45) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+ static_cast<T>(v45_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray45& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+ const T43 v43_;
+ const T44 v44_;
+ const T45 v45_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46>
+class ValueArray46 {
+ public:
+ ValueArray46(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) : v1_(v1), v2_(v2), v3_(v3),
+ v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+ v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+ v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+ v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34),
+ v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40),
+ v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+ static_cast<T>(v45_), static_cast<T>(v46_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray46& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+ const T43 v43_;
+ const T44 v44_;
+ const T45 v45_;
+ const T46 v46_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47>
+class ValueArray47 {
+ public:
+ ValueArray47(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) : v1_(v1), v2_(v2),
+ v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+ v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+ v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+ v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+ v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34),
+ v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40),
+ v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46),
+ v47_(v47) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+ static_cast<T>(v45_), static_cast<T>(v46_), static_cast<T>(v47_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray47& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+ const T43 v43_;
+ const T44 v44_;
+ const T45 v45_;
+ const T46 v46_;
+ const T47 v47_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48>
+class ValueArray48 {
+ public:
+ ValueArray48(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48) : v1_(v1),
+ v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
+ v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
+ v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21),
+ v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27),
+ v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33),
+ v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39),
+ v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45),
+ v46_(v46), v47_(v47), v48_(v48) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+ static_cast<T>(v45_), static_cast<T>(v46_), static_cast<T>(v47_),
+ static_cast<T>(v48_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray48& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+ const T43 v43_;
+ const T44 v44_;
+ const T45 v45_;
+ const T46 v46_;
+ const T47 v47_;
+ const T48 v48_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48, typename T49>
+class ValueArray49 {
+ public:
+ ValueArray49(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48,
+ T49 v49) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+ v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+ v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+ v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38),
+ v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44),
+ v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+ static_cast<T>(v45_), static_cast<T>(v46_), static_cast<T>(v47_),
+ static_cast<T>(v48_), static_cast<T>(v49_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray49& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+ const T43 v43_;
+ const T44 v44_;
+ const T45 v45_;
+ const T46 v46_;
+ const T47 v47_;
+ const T48 v48_;
+ const T49 v49_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48, typename T49, typename T50>
+class ValueArray50 {
+ public:
+ ValueArray50(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+ T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+ T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+ T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+ T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+ T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, T49 v49,
+ T50 v50) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+ v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+ v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+ v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+ v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+ v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38),
+ v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44),
+ v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49), v50_(v50) {}
+
+ template <typename T>
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+ static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+ static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+ static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+ static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+ static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+ static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+ static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+ static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+ static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+ static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+ static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+ static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+ static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+ static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+ static_cast<T>(v45_), static_cast<T>(v46_), static_cast<T>(v47_),
+ static_cast<T>(v48_), static_cast<T>(v49_), static_cast<T>(v50_)};
+ return ValuesIn(array);
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const ValueArray50& other);
+
+ const T1 v1_;
+ const T2 v2_;
+ const T3 v3_;
+ const T4 v4_;
+ const T5 v5_;
+ const T6 v6_;
+ const T7 v7_;
+ const T8 v8_;
+ const T9 v9_;
+ const T10 v10_;
+ const T11 v11_;
+ const T12 v12_;
+ const T13 v13_;
+ const T14 v14_;
+ const T15 v15_;
+ const T16 v16_;
+ const T17 v17_;
+ const T18 v18_;
+ const T19 v19_;
+ const T20 v20_;
+ const T21 v21_;
+ const T22 v22_;
+ const T23 v23_;
+ const T24 v24_;
+ const T25 v25_;
+ const T26 v26_;
+ const T27 v27_;
+ const T28 v28_;
+ const T29 v29_;
+ const T30 v30_;
+ const T31 v31_;
+ const T32 v32_;
+ const T33 v33_;
+ const T34 v34_;
+ const T35 v35_;
+ const T36 v36_;
+ const T37 v37_;
+ const T38 v38_;
+ const T39 v39_;
+ const T40 v40_;
+ const T41 v41_;
+ const T42 v42_;
+ const T43 v43_;
+ const T44 v44_;
+ const T45 v45_;
+ const T46 v46_;
+ const T47 v47_;
+ const T48 v48_;
+ const T49 v49_;
+ const T50 v50_;
+};
+
+# if GTEST_HAS_COMBINE
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Generates values from the Cartesian product of values produced
+// by the argument generators.
+//
+template <typename T1, typename T2>
+class CartesianProductGenerator2
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2> ParamType;
+
+ CartesianProductGenerator2(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2)
+ : g1_(g1), g2_(g2) {}
+ virtual ~CartesianProductGenerator2() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current2_;
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return &current_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator2::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator2& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+}; // class CartesianProductGenerator2
+
+
+template <typename T1, typename T2, typename T3>
+class CartesianProductGenerator3
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2, T3> ParamType;
+
+ CartesianProductGenerator3(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3)
+ : g1_(g1), g2_(g2), g3_(g3) {}
+ virtual ~CartesianProductGenerator3() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+ g3_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2,
+ const ParamGenerator<T3>& g3,
+ const typename ParamGenerator<T3>::iterator& current3)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+ begin3_(g3.begin()), end3_(g3.end()), current3_(current3) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current3_;
+ if (current3_ == end3_) {
+ current3_ = begin3_;
+ ++current2_;
+ }
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return &current_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_ &&
+ current3_ == typed_other->current3_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_),
+ begin3_(other.begin3_),
+ end3_(other.end3_),
+ current3_(other.current3_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_, *current3_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_ ||
+ current3_ == end3_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ const typename ParamGenerator<T3>::iterator begin3_;
+ const typename ParamGenerator<T3>::iterator end3_;
+ typename ParamGenerator<T3>::iterator current3_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator3::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator3& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+ const ParamGenerator<T3> g3_;
+}; // class CartesianProductGenerator3
+
+
+template <typename T1, typename T2, typename T3, typename T4>
+class CartesianProductGenerator4
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2, T3, T4> ParamType;
+
+ CartesianProductGenerator4(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+ const ParamGenerator<T4>& g4)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {}
+ virtual ~CartesianProductGenerator4() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+ g3_.begin(), g4_, g4_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+ g4_, g4_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2,
+ const ParamGenerator<T3>& g3,
+ const typename ParamGenerator<T3>::iterator& current3,
+ const ParamGenerator<T4>& g4,
+ const typename ParamGenerator<T4>::iterator& current4)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+ begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+ begin4_(g4.begin()), end4_(g4.end()), current4_(current4) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current4_;
+ if (current4_ == end4_) {
+ current4_ = begin4_;
+ ++current3_;
+ }
+ if (current3_ == end3_) {
+ current3_ = begin3_;
+ ++current2_;
+ }
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return &current_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_ &&
+ current3_ == typed_other->current3_ &&
+ current4_ == typed_other->current4_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_),
+ begin3_(other.begin3_),
+ end3_(other.end3_),
+ current3_(other.current3_),
+ begin4_(other.begin4_),
+ end4_(other.end4_),
+ current4_(other.current4_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_, *current3_,
+ *current4_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_ ||
+ current3_ == end3_ ||
+ current4_ == end4_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ const typename ParamGenerator<T3>::iterator begin3_;
+ const typename ParamGenerator<T3>::iterator end3_;
+ typename ParamGenerator<T3>::iterator current3_;
+ const typename ParamGenerator<T4>::iterator begin4_;
+ const typename ParamGenerator<T4>::iterator end4_;
+ typename ParamGenerator<T4>::iterator current4_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator4::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator4& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+ const ParamGenerator<T3> g3_;
+ const ParamGenerator<T4> g4_;
+}; // class CartesianProductGenerator4
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+class CartesianProductGenerator5
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2, T3, T4, T5> ParamType;
+
+ CartesianProductGenerator5(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+ const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {}
+ virtual ~CartesianProductGenerator5() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+ g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+ g4_, g4_.end(), g5_, g5_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2,
+ const ParamGenerator<T3>& g3,
+ const typename ParamGenerator<T3>::iterator& current3,
+ const ParamGenerator<T4>& g4,
+ const typename ParamGenerator<T4>::iterator& current4,
+ const ParamGenerator<T5>& g5,
+ const typename ParamGenerator<T5>::iterator& current5)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+ begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+ begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+ begin5_(g5.begin()), end5_(g5.end()), current5_(current5) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current5_;
+ if (current5_ == end5_) {
+ current5_ = begin5_;
+ ++current4_;
+ }
+ if (current4_ == end4_) {
+ current4_ = begin4_;
+ ++current3_;
+ }
+ if (current3_ == end3_) {
+ current3_ = begin3_;
+ ++current2_;
+ }
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return &current_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_ &&
+ current3_ == typed_other->current3_ &&
+ current4_ == typed_other->current4_ &&
+ current5_ == typed_other->current5_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_),
+ begin3_(other.begin3_),
+ end3_(other.end3_),
+ current3_(other.current3_),
+ begin4_(other.begin4_),
+ end4_(other.end4_),
+ current4_(other.current4_),
+ begin5_(other.begin5_),
+ end5_(other.end5_),
+ current5_(other.current5_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_, *current3_,
+ *current4_, *current5_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_ ||
+ current3_ == end3_ ||
+ current4_ == end4_ ||
+ current5_ == end5_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ const typename ParamGenerator<T3>::iterator begin3_;
+ const typename ParamGenerator<T3>::iterator end3_;
+ typename ParamGenerator<T3>::iterator current3_;
+ const typename ParamGenerator<T4>::iterator begin4_;
+ const typename ParamGenerator<T4>::iterator end4_;
+ typename ParamGenerator<T4>::iterator current4_;
+ const typename ParamGenerator<T5>::iterator begin5_;
+ const typename ParamGenerator<T5>::iterator end5_;
+ typename ParamGenerator<T5>::iterator current5_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator5::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator5& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+ const ParamGenerator<T3> g3_;
+ const ParamGenerator<T4> g4_;
+ const ParamGenerator<T5> g5_;
+}; // class CartesianProductGenerator5
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+class CartesianProductGenerator6
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5,
+ T6> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6> ParamType;
+
+ CartesianProductGenerator6(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+ const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
+ const ParamGenerator<T6>& g6)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {}
+ virtual ~CartesianProductGenerator6() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+ g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+ g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2,
+ const ParamGenerator<T3>& g3,
+ const typename ParamGenerator<T3>::iterator& current3,
+ const ParamGenerator<T4>& g4,
+ const typename ParamGenerator<T4>::iterator& current4,
+ const ParamGenerator<T5>& g5,
+ const typename ParamGenerator<T5>::iterator& current5,
+ const ParamGenerator<T6>& g6,
+ const typename ParamGenerator<T6>::iterator& current6)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+ begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+ begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+ begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
+ begin6_(g6.begin()), end6_(g6.end()), current6_(current6) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current6_;
+ if (current6_ == end6_) {
+ current6_ = begin6_;
+ ++current5_;
+ }
+ if (current5_ == end5_) {
+ current5_ = begin5_;
+ ++current4_;
+ }
+ if (current4_ == end4_) {
+ current4_ = begin4_;
+ ++current3_;
+ }
+ if (current3_ == end3_) {
+ current3_ = begin3_;
+ ++current2_;
+ }
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return &current_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_ &&
+ current3_ == typed_other->current3_ &&
+ current4_ == typed_other->current4_ &&
+ current5_ == typed_other->current5_ &&
+ current6_ == typed_other->current6_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_),
+ begin3_(other.begin3_),
+ end3_(other.end3_),
+ current3_(other.current3_),
+ begin4_(other.begin4_),
+ end4_(other.end4_),
+ current4_(other.current4_),
+ begin5_(other.begin5_),
+ end5_(other.end5_),
+ current5_(other.current5_),
+ begin6_(other.begin6_),
+ end6_(other.end6_),
+ current6_(other.current6_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_, *current3_,
+ *current4_, *current5_, *current6_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_ ||
+ current3_ == end3_ ||
+ current4_ == end4_ ||
+ current5_ == end5_ ||
+ current6_ == end6_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ const typename ParamGenerator<T3>::iterator begin3_;
+ const typename ParamGenerator<T3>::iterator end3_;
+ typename ParamGenerator<T3>::iterator current3_;
+ const typename ParamGenerator<T4>::iterator begin4_;
+ const typename ParamGenerator<T4>::iterator end4_;
+ typename ParamGenerator<T4>::iterator current4_;
+ const typename ParamGenerator<T5>::iterator begin5_;
+ const typename ParamGenerator<T5>::iterator end5_;
+ typename ParamGenerator<T5>::iterator current5_;
+ const typename ParamGenerator<T6>::iterator begin6_;
+ const typename ParamGenerator<T6>::iterator end6_;
+ typename ParamGenerator<T6>::iterator current6_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator6::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator6& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+ const ParamGenerator<T3> g3_;
+ const ParamGenerator<T4> g4_;
+ const ParamGenerator<T5> g5_;
+ const ParamGenerator<T6> g6_;
+}; // class CartesianProductGenerator6
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+class CartesianProductGenerator7
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+ T7> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7> ParamType;
+
+ CartesianProductGenerator7(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+ const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
+ const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {}
+ virtual ~CartesianProductGenerator7() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+ g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_,
+ g7_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+ g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2,
+ const ParamGenerator<T3>& g3,
+ const typename ParamGenerator<T3>::iterator& current3,
+ const ParamGenerator<T4>& g4,
+ const typename ParamGenerator<T4>::iterator& current4,
+ const ParamGenerator<T5>& g5,
+ const typename ParamGenerator<T5>::iterator& current5,
+ const ParamGenerator<T6>& g6,
+ const typename ParamGenerator<T6>::iterator& current6,
+ const ParamGenerator<T7>& g7,
+ const typename ParamGenerator<T7>::iterator& current7)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+ begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+ begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+ begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
+ begin6_(g6.begin()), end6_(g6.end()), current6_(current6),
+ begin7_(g7.begin()), end7_(g7.end()), current7_(current7) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current7_;
+ if (current7_ == end7_) {
+ current7_ = begin7_;
+ ++current6_;
+ }
+ if (current6_ == end6_) {
+ current6_ = begin6_;
+ ++current5_;
+ }
+ if (current5_ == end5_) {
+ current5_ = begin5_;
+ ++current4_;
+ }
+ if (current4_ == end4_) {
+ current4_ = begin4_;
+ ++current3_;
+ }
+ if (current3_ == end3_) {
+ current3_ = begin3_;
+ ++current2_;
+ }
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return &current_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_ &&
+ current3_ == typed_other->current3_ &&
+ current4_ == typed_other->current4_ &&
+ current5_ == typed_other->current5_ &&
+ current6_ == typed_other->current6_ &&
+ current7_ == typed_other->current7_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_),
+ begin3_(other.begin3_),
+ end3_(other.end3_),
+ current3_(other.current3_),
+ begin4_(other.begin4_),
+ end4_(other.end4_),
+ current4_(other.current4_),
+ begin5_(other.begin5_),
+ end5_(other.end5_),
+ current5_(other.current5_),
+ begin6_(other.begin6_),
+ end6_(other.end6_),
+ current6_(other.current6_),
+ begin7_(other.begin7_),
+ end7_(other.end7_),
+ current7_(other.current7_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_, *current3_,
+ *current4_, *current5_, *current6_, *current7_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_ ||
+ current3_ == end3_ ||
+ current4_ == end4_ ||
+ current5_ == end5_ ||
+ current6_ == end6_ ||
+ current7_ == end7_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ const typename ParamGenerator<T3>::iterator begin3_;
+ const typename ParamGenerator<T3>::iterator end3_;
+ typename ParamGenerator<T3>::iterator current3_;
+ const typename ParamGenerator<T4>::iterator begin4_;
+ const typename ParamGenerator<T4>::iterator end4_;
+ typename ParamGenerator<T4>::iterator current4_;
+ const typename ParamGenerator<T5>::iterator begin5_;
+ const typename ParamGenerator<T5>::iterator end5_;
+ typename ParamGenerator<T5>::iterator current5_;
+ const typename ParamGenerator<T6>::iterator begin6_;
+ const typename ParamGenerator<T6>::iterator end6_;
+ typename ParamGenerator<T6>::iterator current6_;
+ const typename ParamGenerator<T7>::iterator begin7_;
+ const typename ParamGenerator<T7>::iterator end7_;
+ typename ParamGenerator<T7>::iterator current7_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator7::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator7& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+ const ParamGenerator<T3> g3_;
+ const ParamGenerator<T4> g4_;
+ const ParamGenerator<T5> g5_;
+ const ParamGenerator<T6> g6_;
+ const ParamGenerator<T7> g7_;
+}; // class CartesianProductGenerator7
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+class CartesianProductGenerator8
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+ T7, T8> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8> ParamType;
+
+ CartesianProductGenerator8(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+ const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
+ const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7,
+ const ParamGenerator<T8>& g8)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7),
+ g8_(g8) {}
+ virtual ~CartesianProductGenerator8() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+ g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_,
+ g7_.begin(), g8_, g8_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+ g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_,
+ g8_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2,
+ const ParamGenerator<T3>& g3,
+ const typename ParamGenerator<T3>::iterator& current3,
+ const ParamGenerator<T4>& g4,
+ const typename ParamGenerator<T4>::iterator& current4,
+ const ParamGenerator<T5>& g5,
+ const typename ParamGenerator<T5>::iterator& current5,
+ const ParamGenerator<T6>& g6,
+ const typename ParamGenerator<T6>::iterator& current6,
+ const ParamGenerator<T7>& g7,
+ const typename ParamGenerator<T7>::iterator& current7,
+ const ParamGenerator<T8>& g8,
+ const typename ParamGenerator<T8>::iterator& current8)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+ begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+ begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+ begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
+ begin6_(g6.begin()), end6_(g6.end()), current6_(current6),
+ begin7_(g7.begin()), end7_(g7.end()), current7_(current7),
+ begin8_(g8.begin()), end8_(g8.end()), current8_(current8) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current8_;
+ if (current8_ == end8_) {
+ current8_ = begin8_;
+ ++current7_;
+ }
+ if (current7_ == end7_) {
+ current7_ = begin7_;
+ ++current6_;
+ }
+ if (current6_ == end6_) {
+ current6_ = begin6_;
+ ++current5_;
+ }
+ if (current5_ == end5_) {
+ current5_ = begin5_;
+ ++current4_;
+ }
+ if (current4_ == end4_) {
+ current4_ = begin4_;
+ ++current3_;
+ }
+ if (current3_ == end3_) {
+ current3_ = begin3_;
+ ++current2_;
+ }
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return &current_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_ &&
+ current3_ == typed_other->current3_ &&
+ current4_ == typed_other->current4_ &&
+ current5_ == typed_other->current5_ &&
+ current6_ == typed_other->current6_ &&
+ current7_ == typed_other->current7_ &&
+ current8_ == typed_other->current8_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_),
+ begin3_(other.begin3_),
+ end3_(other.end3_),
+ current3_(other.current3_),
+ begin4_(other.begin4_),
+ end4_(other.end4_),
+ current4_(other.current4_),
+ begin5_(other.begin5_),
+ end5_(other.end5_),
+ current5_(other.current5_),
+ begin6_(other.begin6_),
+ end6_(other.end6_),
+ current6_(other.current6_),
+ begin7_(other.begin7_),
+ end7_(other.end7_),
+ current7_(other.current7_),
+ begin8_(other.begin8_),
+ end8_(other.end8_),
+ current8_(other.current8_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_, *current3_,
+ *current4_, *current5_, *current6_, *current7_, *current8_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_ ||
+ current3_ == end3_ ||
+ current4_ == end4_ ||
+ current5_ == end5_ ||
+ current6_ == end6_ ||
+ current7_ == end7_ ||
+ current8_ == end8_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ const typename ParamGenerator<T3>::iterator begin3_;
+ const typename ParamGenerator<T3>::iterator end3_;
+ typename ParamGenerator<T3>::iterator current3_;
+ const typename ParamGenerator<T4>::iterator begin4_;
+ const typename ParamGenerator<T4>::iterator end4_;
+ typename ParamGenerator<T4>::iterator current4_;
+ const typename ParamGenerator<T5>::iterator begin5_;
+ const typename ParamGenerator<T5>::iterator end5_;
+ typename ParamGenerator<T5>::iterator current5_;
+ const typename ParamGenerator<T6>::iterator begin6_;
+ const typename ParamGenerator<T6>::iterator end6_;
+ typename ParamGenerator<T6>::iterator current6_;
+ const typename ParamGenerator<T7>::iterator begin7_;
+ const typename ParamGenerator<T7>::iterator end7_;
+ typename ParamGenerator<T7>::iterator current7_;
+ const typename ParamGenerator<T8>::iterator begin8_;
+ const typename ParamGenerator<T8>::iterator end8_;
+ typename ParamGenerator<T8>::iterator current8_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator8::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator8& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+ const ParamGenerator<T3> g3_;
+ const ParamGenerator<T4> g4_;
+ const ParamGenerator<T5> g5_;
+ const ParamGenerator<T6> g6_;
+ const ParamGenerator<T7> g7_;
+ const ParamGenerator<T8> g8_;
+}; // class CartesianProductGenerator8
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+class CartesianProductGenerator9
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+ T7, T8, T9> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9> ParamType;
+
+ CartesianProductGenerator9(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+ const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
+ const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7,
+ const ParamGenerator<T8>& g8, const ParamGenerator<T9>& g9)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8),
+ g9_(g9) {}
+ virtual ~CartesianProductGenerator9() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+ g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_,
+ g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+ g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_,
+ g8_.end(), g9_, g9_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2,
+ const ParamGenerator<T3>& g3,
+ const typename ParamGenerator<T3>::iterator& current3,
+ const ParamGenerator<T4>& g4,
+ const typename ParamGenerator<T4>::iterator& current4,
+ const ParamGenerator<T5>& g5,
+ const typename ParamGenerator<T5>::iterator& current5,
+ const ParamGenerator<T6>& g6,
+ const typename ParamGenerator<T6>::iterator& current6,
+ const ParamGenerator<T7>& g7,
+ const typename ParamGenerator<T7>::iterator& current7,
+ const ParamGenerator<T8>& g8,
+ const typename ParamGenerator<T8>::iterator& current8,
+ const ParamGenerator<T9>& g9,
+ const typename ParamGenerator<T9>::iterator& current9)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+ begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+ begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+ begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
+ begin6_(g6.begin()), end6_(g6.end()), current6_(current6),
+ begin7_(g7.begin()), end7_(g7.end()), current7_(current7),
+ begin8_(g8.begin()), end8_(g8.end()), current8_(current8),
+ begin9_(g9.begin()), end9_(g9.end()), current9_(current9) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current9_;
+ if (current9_ == end9_) {
+ current9_ = begin9_;
+ ++current8_;
+ }
+ if (current8_ == end8_) {
+ current8_ = begin8_;
+ ++current7_;
+ }
+ if (current7_ == end7_) {
+ current7_ = begin7_;
+ ++current6_;
+ }
+ if (current6_ == end6_) {
+ current6_ = begin6_;
+ ++current5_;
+ }
+ if (current5_ == end5_) {
+ current5_ = begin5_;
+ ++current4_;
+ }
+ if (current4_ == end4_) {
+ current4_ = begin4_;
+ ++current3_;
+ }
+ if (current3_ == end3_) {
+ current3_ = begin3_;
+ ++current2_;
+ }
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return &current_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_ &&
+ current3_ == typed_other->current3_ &&
+ current4_ == typed_other->current4_ &&
+ current5_ == typed_other->current5_ &&
+ current6_ == typed_other->current6_ &&
+ current7_ == typed_other->current7_ &&
+ current8_ == typed_other->current8_ &&
+ current9_ == typed_other->current9_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_),
+ begin3_(other.begin3_),
+ end3_(other.end3_),
+ current3_(other.current3_),
+ begin4_(other.begin4_),
+ end4_(other.end4_),
+ current4_(other.current4_),
+ begin5_(other.begin5_),
+ end5_(other.end5_),
+ current5_(other.current5_),
+ begin6_(other.begin6_),
+ end6_(other.end6_),
+ current6_(other.current6_),
+ begin7_(other.begin7_),
+ end7_(other.end7_),
+ current7_(other.current7_),
+ begin8_(other.begin8_),
+ end8_(other.end8_),
+ current8_(other.current8_),
+ begin9_(other.begin9_),
+ end9_(other.end9_),
+ current9_(other.current9_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_, *current3_,
+ *current4_, *current5_, *current6_, *current7_, *current8_,
+ *current9_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_ ||
+ current3_ == end3_ ||
+ current4_ == end4_ ||
+ current5_ == end5_ ||
+ current6_ == end6_ ||
+ current7_ == end7_ ||
+ current8_ == end8_ ||
+ current9_ == end9_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ const typename ParamGenerator<T3>::iterator begin3_;
+ const typename ParamGenerator<T3>::iterator end3_;
+ typename ParamGenerator<T3>::iterator current3_;
+ const typename ParamGenerator<T4>::iterator begin4_;
+ const typename ParamGenerator<T4>::iterator end4_;
+ typename ParamGenerator<T4>::iterator current4_;
+ const typename ParamGenerator<T5>::iterator begin5_;
+ const typename ParamGenerator<T5>::iterator end5_;
+ typename ParamGenerator<T5>::iterator current5_;
+ const typename ParamGenerator<T6>::iterator begin6_;
+ const typename ParamGenerator<T6>::iterator end6_;
+ typename ParamGenerator<T6>::iterator current6_;
+ const typename ParamGenerator<T7>::iterator begin7_;
+ const typename ParamGenerator<T7>::iterator end7_;
+ typename ParamGenerator<T7>::iterator current7_;
+ const typename ParamGenerator<T8>::iterator begin8_;
+ const typename ParamGenerator<T8>::iterator end8_;
+ typename ParamGenerator<T8>::iterator current8_;
+ const typename ParamGenerator<T9>::iterator begin9_;
+ const typename ParamGenerator<T9>::iterator end9_;
+ typename ParamGenerator<T9>::iterator current9_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator9::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator9& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+ const ParamGenerator<T3> g3_;
+ const ParamGenerator<T4> g4_;
+ const ParamGenerator<T5> g5_;
+ const ParamGenerator<T6> g6_;
+ const ParamGenerator<T7> g7_;
+ const ParamGenerator<T8> g8_;
+ const ParamGenerator<T9> g9_;
+}; // class CartesianProductGenerator9
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+class CartesianProductGenerator10
+ : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+ T7, T8, T9, T10> > {
+ public:
+ typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> ParamType;
+
+ CartesianProductGenerator10(const ParamGenerator<T1>& g1,
+ const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+ const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
+ const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7,
+ const ParamGenerator<T8>& g8, const ParamGenerator<T9>& g9,
+ const ParamGenerator<T10>& g10)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8),
+ g9_(g9), g10_(g10) {}
+ virtual ~CartesianProductGenerator10() {}
+
+ virtual ParamIteratorInterface<ParamType>* Begin() const {
+ return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+ g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_,
+ g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin(), g10_, g10_.begin());
+ }
+ virtual ParamIteratorInterface<ParamType>* End() const {
+ return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+ g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_,
+ g8_.end(), g9_, g9_.end(), g10_, g10_.end());
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<ParamType> {
+ public:
+ Iterator(const ParamGeneratorInterface<ParamType>* base,
+ const ParamGenerator<T1>& g1,
+ const typename ParamGenerator<T1>::iterator& current1,
+ const ParamGenerator<T2>& g2,
+ const typename ParamGenerator<T2>::iterator& current2,
+ const ParamGenerator<T3>& g3,
+ const typename ParamGenerator<T3>::iterator& current3,
+ const ParamGenerator<T4>& g4,
+ const typename ParamGenerator<T4>::iterator& current4,
+ const ParamGenerator<T5>& g5,
+ const typename ParamGenerator<T5>::iterator& current5,
+ const ParamGenerator<T6>& g6,
+ const typename ParamGenerator<T6>::iterator& current6,
+ const ParamGenerator<T7>& g7,
+ const typename ParamGenerator<T7>::iterator& current7,
+ const ParamGenerator<T8>& g8,
+ const typename ParamGenerator<T8>::iterator& current8,
+ const ParamGenerator<T9>& g9,
+ const typename ParamGenerator<T9>::iterator& current9,
+ const ParamGenerator<T10>& g10,
+ const typename ParamGenerator<T10>::iterator& current10)
+ : base_(base),
+ begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+ begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+ begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+ begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+ begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
+ begin6_(g6.begin()), end6_(g6.end()), current6_(current6),
+ begin7_(g7.begin()), end7_(g7.end()), current7_(current7),
+ begin8_(g8.begin()), end8_(g8.end()), current8_(current8),
+ begin9_(g9.begin()), end9_(g9.end()), current9_(current9),
+ begin10_(g10.begin()), end10_(g10.end()), current10_(current10) {
+ ComputeCurrentValue();
+ }
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+ return base_;
+ }
+ // Advance should not be called on beyond-of-range iterators
+ // so no component iterators must be beyond end of range, either.
+ virtual void Advance() {
+ assert(!AtEnd());
+ ++current10_;
+ if (current10_ == end10_) {
+ current10_ = begin10_;
+ ++current9_;
+ }
+ if (current9_ == end9_) {
+ current9_ = begin9_;
+ ++current8_;
+ }
+ if (current8_ == end8_) {
+ current8_ = begin8_;
+ ++current7_;
+ }
+ if (current7_ == end7_) {
+ current7_ = begin7_;
+ ++current6_;
+ }
+ if (current6_ == end6_) {
+ current6_ = begin6_;
+ ++current5_;
+ }
+ if (current5_ == end5_) {
+ current5_ = begin5_;
+ ++current4_;
+ }
+ if (current4_ == end4_) {
+ current4_ = begin4_;
+ ++current3_;
+ }
+ if (current3_ == end3_) {
+ current3_ = begin3_;
+ ++current2_;
+ }
+ if (current2_ == end2_) {
+ current2_ = begin2_;
+ ++current1_;
+ }
+ ComputeCurrentValue();
+ }
+ virtual ParamIteratorInterface<ParamType>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const ParamType* Current() const { return &current_value_; }
+ virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const Iterator* typed_other =
+ CheckedDowncastToActualType<const Iterator>(&other);
+ // We must report iterators equal if they both point beyond their
+ // respective ranges. That can happen in a variety of fashions,
+ // so we have to consult AtEnd().
+ return (AtEnd() && typed_other->AtEnd()) ||
+ (
+ current1_ == typed_other->current1_ &&
+ current2_ == typed_other->current2_ &&
+ current3_ == typed_other->current3_ &&
+ current4_ == typed_other->current4_ &&
+ current5_ == typed_other->current5_ &&
+ current6_ == typed_other->current6_ &&
+ current7_ == typed_other->current7_ &&
+ current8_ == typed_other->current8_ &&
+ current9_ == typed_other->current9_ &&
+ current10_ == typed_other->current10_);
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : base_(other.base_),
+ begin1_(other.begin1_),
+ end1_(other.end1_),
+ current1_(other.current1_),
+ begin2_(other.begin2_),
+ end2_(other.end2_),
+ current2_(other.current2_),
+ begin3_(other.begin3_),
+ end3_(other.end3_),
+ current3_(other.current3_),
+ begin4_(other.begin4_),
+ end4_(other.end4_),
+ current4_(other.current4_),
+ begin5_(other.begin5_),
+ end5_(other.end5_),
+ current5_(other.current5_),
+ begin6_(other.begin6_),
+ end6_(other.end6_),
+ current6_(other.current6_),
+ begin7_(other.begin7_),
+ end7_(other.end7_),
+ current7_(other.current7_),
+ begin8_(other.begin8_),
+ end8_(other.end8_),
+ current8_(other.current8_),
+ begin9_(other.begin9_),
+ end9_(other.end9_),
+ current9_(other.current9_),
+ begin10_(other.begin10_),
+ end10_(other.end10_),
+ current10_(other.current10_) {
+ ComputeCurrentValue();
+ }
+
+ void ComputeCurrentValue() {
+ if (!AtEnd())
+ current_value_ = ParamType(*current1_, *current2_, *current3_,
+ *current4_, *current5_, *current6_, *current7_, *current8_,
+ *current9_, *current10_);
+ }
+ bool AtEnd() const {
+ // We must report iterator past the end of the range when either of the
+ // component iterators has reached the end of its range.
+ return
+ current1_ == end1_ ||
+ current2_ == end2_ ||
+ current3_ == end3_ ||
+ current4_ == end4_ ||
+ current5_ == end5_ ||
+ current6_ == end6_ ||
+ current7_ == end7_ ||
+ current8_ == end8_ ||
+ current9_ == end9_ ||
+ current10_ == end10_;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<ParamType>* const base_;
+ // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+ // current[i]_ is the actual traversing iterator.
+ const typename ParamGenerator<T1>::iterator begin1_;
+ const typename ParamGenerator<T1>::iterator end1_;
+ typename ParamGenerator<T1>::iterator current1_;
+ const typename ParamGenerator<T2>::iterator begin2_;
+ const typename ParamGenerator<T2>::iterator end2_;
+ typename ParamGenerator<T2>::iterator current2_;
+ const typename ParamGenerator<T3>::iterator begin3_;
+ const typename ParamGenerator<T3>::iterator end3_;
+ typename ParamGenerator<T3>::iterator current3_;
+ const typename ParamGenerator<T4>::iterator begin4_;
+ const typename ParamGenerator<T4>::iterator end4_;
+ typename ParamGenerator<T4>::iterator current4_;
+ const typename ParamGenerator<T5>::iterator begin5_;
+ const typename ParamGenerator<T5>::iterator end5_;
+ typename ParamGenerator<T5>::iterator current5_;
+ const typename ParamGenerator<T6>::iterator begin6_;
+ const typename ParamGenerator<T6>::iterator end6_;
+ typename ParamGenerator<T6>::iterator current6_;
+ const typename ParamGenerator<T7>::iterator begin7_;
+ const typename ParamGenerator<T7>::iterator end7_;
+ typename ParamGenerator<T7>::iterator current7_;
+ const typename ParamGenerator<T8>::iterator begin8_;
+ const typename ParamGenerator<T8>::iterator end8_;
+ typename ParamGenerator<T8>::iterator current8_;
+ const typename ParamGenerator<T9>::iterator begin9_;
+ const typename ParamGenerator<T9>::iterator end9_;
+ typename ParamGenerator<T9>::iterator current9_;
+ const typename ParamGenerator<T10>::iterator begin10_;
+ const typename ParamGenerator<T10>::iterator end10_;
+ typename ParamGenerator<T10>::iterator current10_;
+ ParamType current_value_;
+ }; // class CartesianProductGenerator10::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductGenerator10& other);
+
+ const ParamGenerator<T1> g1_;
+ const ParamGenerator<T2> g2_;
+ const ParamGenerator<T3> g3_;
+ const ParamGenerator<T4> g4_;
+ const ParamGenerator<T5> g5_;
+ const ParamGenerator<T6> g6_;
+ const ParamGenerator<T7> g7_;
+ const ParamGenerator<T8> g8_;
+ const ParamGenerator<T9> g9_;
+ const ParamGenerator<T10> g10_;
+}; // class CartesianProductGenerator10
+
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Helper classes providing Combine() with polymorphic features. They allow
+// casting CartesianProductGeneratorN<T> to ParamGenerator<U> if T is
+// convertible to U.
+//
+template <class Generator1, class Generator2>
+class CartesianProductHolder2 {
+ public:
+CartesianProductHolder2(const Generator1& g1, const Generator2& g2)
+ : g1_(g1), g2_(g2) {}
+ template <typename T1, typename T2>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2> >(
+ new CartesianProductGenerator2<T1, T2>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder2& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+}; // class CartesianProductHolder2
+
+template <class Generator1, class Generator2, class Generator3>
+class CartesianProductHolder3 {
+ public:
+CartesianProductHolder3(const Generator1& g1, const Generator2& g2,
+ const Generator3& g3)
+ : g1_(g1), g2_(g2), g3_(g3) {}
+ template <typename T1, typename T2, typename T3>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2, T3> >(
+ new CartesianProductGenerator3<T1, T2, T3>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_),
+ static_cast<ParamGenerator<T3> >(g3_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder3& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+ const Generator3 g3_;
+}; // class CartesianProductHolder3
+
+template <class Generator1, class Generator2, class Generator3,
+ class Generator4>
+class CartesianProductHolder4 {
+ public:
+CartesianProductHolder4(const Generator1& g1, const Generator2& g2,
+ const Generator3& g3, const Generator4& g4)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {}
+ template <typename T1, typename T2, typename T3, typename T4>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4> >(
+ new CartesianProductGenerator4<T1, T2, T3, T4>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_),
+ static_cast<ParamGenerator<T3> >(g3_),
+ static_cast<ParamGenerator<T4> >(g4_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder4& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+ const Generator3 g3_;
+ const Generator4 g4_;
+}; // class CartesianProductHolder4
+
+template <class Generator1, class Generator2, class Generator3,
+ class Generator4, class Generator5>
+class CartesianProductHolder5 {
+ public:
+CartesianProductHolder5(const Generator1& g1, const Generator2& g2,
+ const Generator3& g3, const Generator4& g4, const Generator5& g5)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {}
+ template <typename T1, typename T2, typename T3, typename T4, typename T5>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5> >(
+ new CartesianProductGenerator5<T1, T2, T3, T4, T5>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_),
+ static_cast<ParamGenerator<T3> >(g3_),
+ static_cast<ParamGenerator<T4> >(g4_),
+ static_cast<ParamGenerator<T5> >(g5_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder5& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+ const Generator3 g3_;
+ const Generator4 g4_;
+ const Generator5 g5_;
+}; // class CartesianProductHolder5
+
+template <class Generator1, class Generator2, class Generator3,
+ class Generator4, class Generator5, class Generator6>
+class CartesianProductHolder6 {
+ public:
+CartesianProductHolder6(const Generator1& g1, const Generator2& g2,
+ const Generator3& g3, const Generator4& g4, const Generator5& g5,
+ const Generator6& g6)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {}
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6> >(
+ new CartesianProductGenerator6<T1, T2, T3, T4, T5, T6>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_),
+ static_cast<ParamGenerator<T3> >(g3_),
+ static_cast<ParamGenerator<T4> >(g4_),
+ static_cast<ParamGenerator<T5> >(g5_),
+ static_cast<ParamGenerator<T6> >(g6_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder6& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+ const Generator3 g3_;
+ const Generator4 g4_;
+ const Generator5 g5_;
+ const Generator6 g6_;
+}; // class CartesianProductHolder6
+
+template <class Generator1, class Generator2, class Generator3,
+ class Generator4, class Generator5, class Generator6, class Generator7>
+class CartesianProductHolder7 {
+ public:
+CartesianProductHolder7(const Generator1& g1, const Generator2& g2,
+ const Generator3& g3, const Generator4& g4, const Generator5& g5,
+ const Generator6& g6, const Generator7& g7)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {}
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+ T7> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7> >(
+ new CartesianProductGenerator7<T1, T2, T3, T4, T5, T6, T7>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_),
+ static_cast<ParamGenerator<T3> >(g3_),
+ static_cast<ParamGenerator<T4> >(g4_),
+ static_cast<ParamGenerator<T5> >(g5_),
+ static_cast<ParamGenerator<T6> >(g6_),
+ static_cast<ParamGenerator<T7> >(g7_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder7& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+ const Generator3 g3_;
+ const Generator4 g4_;
+ const Generator5 g5_;
+ const Generator6 g6_;
+ const Generator7 g7_;
+}; // class CartesianProductHolder7
+
+template <class Generator1, class Generator2, class Generator3,
+ class Generator4, class Generator5, class Generator6, class Generator7,
+ class Generator8>
+class CartesianProductHolder8 {
+ public:
+CartesianProductHolder8(const Generator1& g1, const Generator2& g2,
+ const Generator3& g3, const Generator4& g4, const Generator5& g5,
+ const Generator6& g6, const Generator7& g7, const Generator8& g8)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7),
+ g8_(g8) {}
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7,
+ T8> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8> >(
+ new CartesianProductGenerator8<T1, T2, T3, T4, T5, T6, T7, T8>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_),
+ static_cast<ParamGenerator<T3> >(g3_),
+ static_cast<ParamGenerator<T4> >(g4_),
+ static_cast<ParamGenerator<T5> >(g5_),
+ static_cast<ParamGenerator<T6> >(g6_),
+ static_cast<ParamGenerator<T7> >(g7_),
+ static_cast<ParamGenerator<T8> >(g8_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder8& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+ const Generator3 g3_;
+ const Generator4 g4_;
+ const Generator5 g5_;
+ const Generator6 g6_;
+ const Generator7 g7_;
+ const Generator8 g8_;
+}; // class CartesianProductHolder8
+
+template <class Generator1, class Generator2, class Generator3,
+ class Generator4, class Generator5, class Generator6, class Generator7,
+ class Generator8, class Generator9>
+class CartesianProductHolder9 {
+ public:
+CartesianProductHolder9(const Generator1& g1, const Generator2& g2,
+ const Generator3& g3, const Generator4& g4, const Generator5& g5,
+ const Generator6& g6, const Generator7& g7, const Generator8& g8,
+ const Generator9& g9)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8),
+ g9_(g9) {}
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
+ T9> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
+ T9> >(
+ new CartesianProductGenerator9<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_),
+ static_cast<ParamGenerator<T3> >(g3_),
+ static_cast<ParamGenerator<T4> >(g4_),
+ static_cast<ParamGenerator<T5> >(g5_),
+ static_cast<ParamGenerator<T6> >(g6_),
+ static_cast<ParamGenerator<T7> >(g7_),
+ static_cast<ParamGenerator<T8> >(g8_),
+ static_cast<ParamGenerator<T9> >(g9_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder9& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+ const Generator3 g3_;
+ const Generator4 g4_;
+ const Generator5 g5_;
+ const Generator6 g6_;
+ const Generator7 g7_;
+ const Generator8 g8_;
+ const Generator9 g9_;
+}; // class CartesianProductHolder9
+
+template <class Generator1, class Generator2, class Generator3,
+ class Generator4, class Generator5, class Generator6, class Generator7,
+ class Generator8, class Generator9, class Generator10>
+class CartesianProductHolder10 {
+ public:
+CartesianProductHolder10(const Generator1& g1, const Generator2& g2,
+ const Generator3& g3, const Generator4& g4, const Generator5& g5,
+ const Generator6& g6, const Generator7& g7, const Generator8& g8,
+ const Generator9& g9, const Generator10& g10)
+ : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8),
+ g9_(g9), g10_(g10) {}
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+ operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
+ T9, T10> >() const {
+ return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
+ T9, T10> >(
+ new CartesianProductGenerator10<T1, T2, T3, T4, T5, T6, T7, T8, T9,
+ T10>(
+ static_cast<ParamGenerator<T1> >(g1_),
+ static_cast<ParamGenerator<T2> >(g2_),
+ static_cast<ParamGenerator<T3> >(g3_),
+ static_cast<ParamGenerator<T4> >(g4_),
+ static_cast<ParamGenerator<T5> >(g5_),
+ static_cast<ParamGenerator<T6> >(g6_),
+ static_cast<ParamGenerator<T7> >(g7_),
+ static_cast<ParamGenerator<T8> >(g8_),
+ static_cast<ParamGenerator<T9> >(g9_),
+ static_cast<ParamGenerator<T10> >(g10_)));
+ }
+
+ private:
+ // No implementation - assignment is unsupported.
+ void operator=(const CartesianProductHolder10& other);
+
+ const Generator1 g1_;
+ const Generator2 g2_;
+ const Generator3 g3_;
+ const Generator4 g4_;
+ const Generator5 g5_;
+ const Generator6 g6_;
+ const Generator7 g7_;
+ const Generator8 g8_;
+ const Generator9 g9_;
+ const Generator10 g10_;
+}; // class CartesianProductHolder10
+
+# endif // GTEST_HAS_COMBINE
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_HAS_PARAM_TEST
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-param-util.h b/extern/gtest/include/gtest/internal/gtest-param-util.h
new file mode 100644
index 00000000000..d5e1028b0c1
--- /dev/null
+++ b/extern/gtest/include/gtest/internal/gtest-param-util.h
@@ -0,0 +1,619 @@
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: vladl@google.com (Vlad Losev)
+
+// Type and function utilities for implementing parameterized tests.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
+
+#include <iterator>
+#include <utility>
+#include <vector>
+
+// scripts/fuse_gtest.py depends on gtest's own header being #included
+// *unconditionally*. Therefore these #includes cannot be moved
+// inside #if GTEST_HAS_PARAM_TEST.
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-linked_ptr.h"
+#include "gtest/internal/gtest-port.h"
+#include "gtest/gtest-printers.h"
+
+#if GTEST_HAS_PARAM_TEST
+
+namespace testing {
+namespace internal {
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Outputs a message explaining invalid registration of different
+// fixture class for the same test case. This may happen when
+// TEST_P macro is used to define two tests with the same name
+// but in different namespaces.
+GTEST_API_ void ReportInvalidTestCaseType(const char* test_case_name,
+ const char* file, int line);
+
+template <typename> class ParamGeneratorInterface;
+template <typename> class ParamGenerator;
+
+// Interface for iterating over elements provided by an implementation
+// of ParamGeneratorInterface<T>.
+template <typename T>
+class ParamIteratorInterface {
+ public:
+ virtual ~ParamIteratorInterface() {}
+ // A pointer to the base generator instance.
+ // Used only for the purposes of iterator comparison
+ // to make sure that two iterators belong to the same generator.
+ virtual const ParamGeneratorInterface<T>* BaseGenerator() const = 0;
+ // Advances iterator to point to the next element
+ // provided by the generator. The caller is responsible
+ // for not calling Advance() on an iterator equal to
+ // BaseGenerator()->End().
+ virtual void Advance() = 0;
+ // Clones the iterator object. Used for implementing copy semantics
+ // of ParamIterator<T>.
+ virtual ParamIteratorInterface* Clone() const = 0;
+ // Dereferences the current iterator and provides (read-only) access
+ // to the pointed value. It is the caller's responsibility not to call
+ // Current() on an iterator equal to BaseGenerator()->End().
+ // Used for implementing ParamGenerator<T>::operator*().
+ virtual const T* Current() const = 0;
+ // Determines whether the given iterator and other point to the same
+ // element in the sequence generated by the generator.
+ // Used for implementing ParamGenerator<T>::operator==().
+ virtual bool Equals(const ParamIteratorInterface& other) const = 0;
+};
+
+// Class iterating over elements provided by an implementation of
+// ParamGeneratorInterface<T>. It wraps ParamIteratorInterface<T>
+// and implements the const forward iterator concept.
+template <typename T>
+class ParamIterator {
+ public:
+ typedef T value_type;
+ typedef const T& reference;
+ typedef ptrdiff_t difference_type;
+
+ // ParamIterator assumes ownership of the impl_ pointer.
+ ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {}
+ ParamIterator& operator=(const ParamIterator& other) {
+ if (this != &other)
+ impl_.reset(other.impl_->Clone());
+ return *this;
+ }
+
+ const T& operator*() const { return *impl_->Current(); }
+ const T* operator->() const { return impl_->Current(); }
+ // Prefix version of operator++.
+ ParamIterator& operator++() {
+ impl_->Advance();
+ return *this;
+ }
+ // Postfix version of operator++.
+ ParamIterator operator++(int /*unused*/) {
+ ParamIteratorInterface<T>* clone = impl_->Clone();
+ impl_->Advance();
+ return ParamIterator(clone);
+ }
+ bool operator==(const ParamIterator& other) const {
+ return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_);
+ }
+ bool operator!=(const ParamIterator& other) const {
+ return !(*this == other);
+ }
+
+ private:
+ friend class ParamGenerator<T>;
+ explicit ParamIterator(ParamIteratorInterface<T>* impl) : impl_(impl) {}
+ scoped_ptr<ParamIteratorInterface<T> > impl_;
+};
+
+// ParamGeneratorInterface<T> is the binary interface to access generators
+// defined in other translation units.
+template <typename T>
+class ParamGeneratorInterface {
+ public:
+ typedef T ParamType;
+
+ virtual ~ParamGeneratorInterface() {}
+
+ // Generator interface definition
+ virtual ParamIteratorInterface<T>* Begin() const = 0;
+ virtual ParamIteratorInterface<T>* End() const = 0;
+};
+
+// Wraps ParamGeneratorInterface<T> and provides general generator syntax
+// compatible with the STL Container concept.
+// This class implements copy initialization semantics and the contained
+// ParamGeneratorInterface<T> instance is shared among all copies
+// of the original object. This is possible because that instance is immutable.
+template<typename T>
+class ParamGenerator {
+ public:
+ typedef ParamIterator<T> iterator;
+
+ explicit ParamGenerator(ParamGeneratorInterface<T>* impl) : impl_(impl) {}
+ ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {}
+
+ ParamGenerator& operator=(const ParamGenerator& other) {
+ impl_ = other.impl_;
+ return *this;
+ }
+
+ iterator begin() const { return iterator(impl_->Begin()); }
+ iterator end() const { return iterator(impl_->End()); }
+
+ private:
+ linked_ptr<const ParamGeneratorInterface<T> > impl_;
+};
+
+// Generates values from a range of two comparable values. Can be used to
+// generate sequences of user-defined types that implement operator+() and
+// operator<().
+// This class is used in the Range() function.
+template <typename T, typename IncrementT>
+class RangeGenerator : public ParamGeneratorInterface<T> {
+ public:
+ RangeGenerator(T begin, T end, IncrementT step)
+ : begin_(begin), end_(end),
+ step_(step), end_index_(CalculateEndIndex(begin, end, step)) {}
+ virtual ~RangeGenerator() {}
+
+ virtual ParamIteratorInterface<T>* Begin() const {
+ return new Iterator(this, begin_, 0, step_);
+ }
+ virtual ParamIteratorInterface<T>* End() const {
+ return new Iterator(this, end_, end_index_, step_);
+ }
+
+ private:
+ class Iterator : public ParamIteratorInterface<T> {
+ public:
+ Iterator(const ParamGeneratorInterface<T>* base, T value, int index,
+ IncrementT step)
+ : base_(base), value_(value), index_(index), step_(step) {}
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<T>* BaseGenerator() const {
+ return base_;
+ }
+ virtual void Advance() {
+ value_ = value_ + step_;
+ index_++;
+ }
+ virtual ParamIteratorInterface<T>* Clone() const {
+ return new Iterator(*this);
+ }
+ virtual const T* Current() const { return &value_; }
+ virtual bool Equals(const ParamIteratorInterface<T>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ const int other_index =
+ CheckedDowncastToActualType<const Iterator>(&other)->index_;
+ return index_ == other_index;
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ : ParamIteratorInterface<T>(),
+ base_(other.base_), value_(other.value_), index_(other.index_),
+ step_(other.step_) {}
+
+ // No implementation - assignment is unsupported.
+ void operator=(const Iterator& other);
+
+ const ParamGeneratorInterface<T>* const base_;
+ T value_;
+ int index_;
+ const IncrementT step_;
+ }; // class RangeGenerator::Iterator
+
+ static int CalculateEndIndex(const T& begin,
+ const T& end,
+ const IncrementT& step) {
+ int end_index = 0;
+ for (T i = begin; i < end; i = i + step)
+ end_index++;
+ return end_index;
+ }
+
+ // No implementation - assignment is unsupported.
+ void operator=(const RangeGenerator& other);
+
+ const T begin_;
+ const T end_;
+ const IncrementT step_;
+ // The index for the end() iterator. All the elements in the generated
+ // sequence are indexed (0-based) to aid iterator comparison.
+ const int end_index_;
+}; // class RangeGenerator
+
+
+// Generates values from a pair of STL-style iterators. Used in the
+// ValuesIn() function. The elements are copied from the source range
+// since the source can be located on the stack, and the generator
+// is likely to persist beyond that stack frame.
+template <typename T>
+class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
+ public:
+ template <typename ForwardIterator>
+ ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end)
+ : container_(begin, end) {}
+ virtual ~ValuesInIteratorRangeGenerator() {}
+
+ virtual ParamIteratorInterface<T>* Begin() const {
+ return new Iterator(this, container_.begin());
+ }
+ virtual ParamIteratorInterface<T>* End() const {
+ return new Iterator(this, container_.end());
+ }
+
+ private:
+ typedef typename ::std::vector<T> ContainerType;
+
+ class Iterator : public ParamIteratorInterface<T> {
+ public:
+ Iterator(const ParamGeneratorInterface<T>* base,
+ typename ContainerType::const_iterator iterator)
+ : base_(base), iterator_(iterator) {}
+ virtual ~Iterator() {}
+
+ virtual const ParamGeneratorInterface<T>* BaseGenerator() const {
+ return base_;
+ }
+ virtual void Advance() {
+ ++iterator_;
+ value_.reset();
+ }
+ virtual ParamIteratorInterface<T>* Clone() const {
+ return new Iterator(*this);
+ }
+ // We need to use cached value referenced by iterator_ because *iterator_
+ // can return a temporary object (and of type other then T), so just
+ // having "return &*iterator_;" doesn't work.
+ // value_ is updated here and not in Advance() because Advance()
+ // can advance iterator_ beyond the end of the range, and we cannot
+ // detect that fact. The client code, on the other hand, is
+ // responsible for not calling Current() on an out-of-range iterator.
+ virtual const T* Current() const {
+ if (value_.get() == NULL)
+ value_.reset(new T(*iterator_));
+ return value_.get();
+ }
+ virtual bool Equals(const ParamIteratorInterface<T>& other) const {
+ // Having the same base generator guarantees that the other
+ // iterator is of the same type and we can downcast.
+ GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+ << "The program attempted to compare iterators "
+ << "from different generators." << std::endl;
+ return iterator_ ==
+ CheckedDowncastToActualType<const Iterator>(&other)->iterator_;
+ }
+
+ private:
+ Iterator(const Iterator& other)
+ // The explicit constructor call suppresses a false warning
+ // emitted by gcc when supplied with the -Wextra option.
+ : ParamIteratorInterface<T>(),
+ base_(other.base_),
+ iterator_(other.iterator_) {}
+
+ const ParamGeneratorInterface<T>* const base_;
+ typename ContainerType::const_iterator iterator_;
+ // A cached value of *iterator_. We keep it here to allow access by
+ // pointer in the wrapping iterator's operator->().
+ // value_ needs to be mutable to be accessed in Current().
+ // Use of scoped_ptr helps manage cached value's lifetime,
+ // which is bound by the lifespan of the iterator itself.
+ mutable scoped_ptr<const T> value_;
+ }; // class ValuesInIteratorRangeGenerator::Iterator
+
+ // No implementation - assignment is unsupported.
+ void operator=(const ValuesInIteratorRangeGenerator& other);
+
+ const ContainerType container_;
+}; // class ValuesInIteratorRangeGenerator
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Stores a parameter value and later creates tests parameterized with that
+// value.
+template <class TestClass>
+class ParameterizedTestFactory : public TestFactoryBase {
+ public:
+ typedef typename TestClass::ParamType ParamType;
+ explicit ParameterizedTestFactory(ParamType parameter) :
+ parameter_(parameter) {}
+ virtual Test* CreateTest() {
+ TestClass::SetParam(&parameter_);
+ return new TestClass();
+ }
+
+ private:
+ const ParamType parameter_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory);
+};
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// TestMetaFactoryBase is a base class for meta-factories that create
+// test factories for passing into MakeAndRegisterTestInfo function.
+template <class ParamType>
+class TestMetaFactoryBase {
+ public:
+ virtual ~TestMetaFactoryBase() {}
+
+ virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0;
+};
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// TestMetaFactory creates test factories for passing into
+// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives
+// ownership of test factory pointer, same factory object cannot be passed
+// into that method twice. But ParameterizedTestCaseInfo is going to call
+// it for each Test/Parameter value combination. Thus it needs meta factory
+// creator class.
+template <class TestCase>
+class TestMetaFactory
+ : public TestMetaFactoryBase<typename TestCase::ParamType> {
+ public:
+ typedef typename TestCase::ParamType ParamType;
+
+ TestMetaFactory() {}
+
+ virtual TestFactoryBase* CreateTestFactory(ParamType parameter) {
+ return new ParameterizedTestFactory<TestCase>(parameter);
+ }
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory);
+};
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// ParameterizedTestCaseInfoBase is a generic interface
+// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase
+// accumulates test information provided by TEST_P macro invocations
+// and generators provided by INSTANTIATE_TEST_CASE_P macro invocations
+// and uses that information to register all resulting test instances
+// in RegisterTests method. The ParameterizeTestCaseRegistry class holds
+// a collection of pointers to the ParameterizedTestCaseInfo objects
+// and calls RegisterTests() on each of them when asked.
+class ParameterizedTestCaseInfoBase {
+ public:
+ virtual ~ParameterizedTestCaseInfoBase() {}
+
+ // Base part of test case name for display purposes.
+ virtual const string& GetTestCaseName() const = 0;
+ // Test case id to verify identity.
+ virtual TypeId GetTestCaseTypeId() const = 0;
+ // UnitTest class invokes this method to register tests in this
+ // test case right before running them in RUN_ALL_TESTS macro.
+ // This method should not be called more then once on any single
+ // instance of a ParameterizedTestCaseInfoBase derived class.
+ virtual void RegisterTests() = 0;
+
+ protected:
+ ParameterizedTestCaseInfoBase() {}
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase);
+};
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// ParameterizedTestCaseInfo accumulates tests obtained from TEST_P
+// macro invocations for a particular test case and generators
+// obtained from INSTANTIATE_TEST_CASE_P macro invocations for that
+// test case. It registers tests with all values generated by all
+// generators when asked.
+template <class TestCase>
+class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
+ public:
+ // ParamType and GeneratorCreationFunc are private types but are required
+ // for declarations of public methods AddTestPattern() and
+ // AddTestCaseInstantiation().
+ typedef typename TestCase::ParamType ParamType;
+ // A function that returns an instance of appropriate generator type.
+ typedef ParamGenerator<ParamType>(GeneratorCreationFunc)();
+
+ explicit ParameterizedTestCaseInfo(const char* name)
+ : test_case_name_(name) {}
+
+ // Test case base name for display purposes.
+ virtual const string& GetTestCaseName() const { return test_case_name_; }
+ // Test case id to verify identity.
+ virtual TypeId GetTestCaseTypeId() const { return GetTypeId<TestCase>(); }
+ // TEST_P macro uses AddTestPattern() to record information
+ // about a single test in a LocalTestInfo structure.
+ // test_case_name is the base name of the test case (without invocation
+ // prefix). test_base_name is the name of an individual test without
+ // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is
+ // test case base name and DoBar is test base name.
+ void AddTestPattern(const char* test_case_name,
+ const char* test_base_name,
+ TestMetaFactoryBase<ParamType>* meta_factory) {
+ tests_.push_back(linked_ptr<TestInfo>(new TestInfo(test_case_name,
+ test_base_name,
+ meta_factory)));
+ }
+ // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information
+ // about a generator.
+ int AddTestCaseInstantiation(const string& instantiation_name,
+ GeneratorCreationFunc* func,
+ const char* /* file */,
+ int /* line */) {
+ instantiations_.push_back(::std::make_pair(instantiation_name, func));
+ return 0; // Return value used only to run this method in namespace scope.
+ }
+ // UnitTest class invokes this method to register tests in this test case
+ // test cases right before running tests in RUN_ALL_TESTS macro.
+ // This method should not be called more then once on any single
+ // instance of a ParameterizedTestCaseInfoBase derived class.
+ // UnitTest has a guard to prevent from calling this method more then once.
+ virtual void RegisterTests() {
+ for (typename TestInfoContainer::iterator test_it = tests_.begin();
+ test_it != tests_.end(); ++test_it) {
+ linked_ptr<TestInfo> test_info = *test_it;
+ for (typename InstantiationContainer::iterator gen_it =
+ instantiations_.begin(); gen_it != instantiations_.end();
+ ++gen_it) {
+ const string& instantiation_name = gen_it->first;
+ ParamGenerator<ParamType> generator((*gen_it->second)());
+
+ string test_case_name;
+ if ( !instantiation_name.empty() )
+ test_case_name = instantiation_name + "/";
+ test_case_name += test_info->test_case_base_name;
+
+ int i = 0;
+ for (typename ParamGenerator<ParamType>::iterator param_it =
+ generator.begin();
+ param_it != generator.end(); ++param_it, ++i) {
+ Message test_name_stream;
+ test_name_stream << test_info->test_base_name << "/" << i;
+ MakeAndRegisterTestInfo(
+ test_case_name.c_str(),
+ test_name_stream.GetString().c_str(),
+ NULL, // No type parameter.
+ PrintToString(*param_it).c_str(),
+ GetTestCaseTypeId(),
+ TestCase::SetUpTestCase,
+ TestCase::TearDownTestCase,
+ test_info->test_meta_factory->CreateTestFactory(*param_it));
+ } // for param_it
+ } // for gen_it
+ } // for test_it
+ } // RegisterTests
+
+ private:
+ // LocalTestInfo structure keeps information about a single test registered
+ // with TEST_P macro.
+ struct TestInfo {
+ TestInfo(const char* a_test_case_base_name,
+ const char* a_test_base_name,
+ TestMetaFactoryBase<ParamType>* a_test_meta_factory) :
+ test_case_base_name(a_test_case_base_name),
+ test_base_name(a_test_base_name),
+ test_meta_factory(a_test_meta_factory) {}
+
+ const string test_case_base_name;
+ const string test_base_name;
+ const scoped_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory;
+ };
+ typedef ::std::vector<linked_ptr<TestInfo> > TestInfoContainer;
+ // Keeps pairs of <Instantiation name, Sequence generator creation function>
+ // received from INSTANTIATE_TEST_CASE_P macros.
+ typedef ::std::vector<std::pair<string, GeneratorCreationFunc*> >
+ InstantiationContainer;
+
+ const string test_case_name_;
+ TestInfoContainer tests_;
+ InstantiationContainer instantiations_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo);
+}; // class ParameterizedTestCaseInfo
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase
+// classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P
+// macros use it to locate their corresponding ParameterizedTestCaseInfo
+// descriptors.
+class ParameterizedTestCaseRegistry {
+ public:
+ ParameterizedTestCaseRegistry() {}
+ ~ParameterizedTestCaseRegistry() {
+ for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
+ it != test_case_infos_.end(); ++it) {
+ delete *it;
+ }
+ }
+
+ // Looks up or creates and returns a structure containing information about
+ // tests and instantiations of a particular test case.
+ template <class TestCase>
+ ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder(
+ const char* test_case_name,
+ const char* file,
+ int line) {
+ ParameterizedTestCaseInfo<TestCase>* typed_test_info = NULL;
+ for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
+ it != test_case_infos_.end(); ++it) {
+ if ((*it)->GetTestCaseName() == test_case_name) {
+ if ((*it)->GetTestCaseTypeId() != GetTypeId<TestCase>()) {
+ // Complain about incorrect usage of Google Test facilities
+ // and terminate the program since we cannot guaranty correct
+ // test case setup and tear-down in this case.
+ ReportInvalidTestCaseType(test_case_name, file, line);
+ posix::Abort();
+ } else {
+ // At this point we are sure that the object we found is of the same
+ // type we are looking for, so we downcast it to that type
+ // without further checks.
+ typed_test_info = CheckedDowncastToActualType<
+ ParameterizedTestCaseInfo<TestCase> >(*it);
+ }
+ break;
+ }
+ }
+ if (typed_test_info == NULL) {
+ typed_test_info = new ParameterizedTestCaseInfo<TestCase>(test_case_name);
+ test_case_infos_.push_back(typed_test_info);
+ }
+ return typed_test_info;
+ }
+ void RegisterTests() {
+ for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
+ it != test_case_infos_.end(); ++it) {
+ (*it)->RegisterTests();
+ }
+ }
+
+ private:
+ typedef ::std::vector<ParameterizedTestCaseInfoBase*> TestCaseInfoContainer;
+
+ TestCaseInfoContainer test_case_infos_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry);
+};
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_HAS_PARAM_TEST
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-port.h b/extern/gtest/include/gtest/internal/gtest-port.h
new file mode 100644
index 00000000000..dc4fe0cb6b8
--- /dev/null
+++ b/extern/gtest/include/gtest/internal/gtest-port.h
@@ -0,0 +1,1947 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: wan@google.com (Zhanyong Wan)
+//
+// Low-level types and utilities for porting Google Test to various
+// platforms. They are subject to change without notice. DO NOT USE
+// THEM IN USER CODE.
+//
+// This file is fundamental to Google Test. All other Google Test source
+// files are expected to #include this. Therefore, it cannot #include
+// any other Google Test header.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
+
+// The user can define the following macros in the build script to
+// control Google Test's behavior. If the user doesn't define a macro
+// in this list, Google Test will define it.
+//
+// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2)
+// is/isn't available.
+// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions
+// are enabled.
+// GTEST_HAS_GLOBAL_STRING - Define it to 1/0 to indicate that ::string
+// is/isn't available (some systems define
+// ::string, which is different to std::string).
+// GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::string
+// is/isn't available (some systems define
+// ::wstring, which is different to std::wstring).
+// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular
+// expressions are/aren't available.
+// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that <pthread.h>
+// is/isn't available.
+// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't
+// enabled.
+// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that
+// std::wstring does/doesn't work (Google Test can
+// be used where std::wstring is unavailable).
+// GTEST_HAS_TR1_TUPLE - Define it to 1/0 to indicate tr1::tuple
+// is/isn't available.
+// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the
+// compiler supports Microsoft's "Structured
+// Exception Handling".
+// GTEST_HAS_STREAM_REDIRECTION
+// - Define it to 1/0 to indicate whether the
+// platform supports I/O stream redirection using
+// dup() and dup2().
+// GTEST_USE_OWN_TR1_TUPLE - Define it to 1/0 to indicate whether Google
+// Test's own tr1 tuple implementation should be
+// used. Unused when the user sets
+// GTEST_HAS_TR1_TUPLE to 0.
+// GTEST_LANG_CXX11 - Define it to 1/0 to indicate that Google Test
+// is building in C++11/C++98 mode.
+// GTEST_LINKED_AS_SHARED_LIBRARY
+// - Define to 1 when compiling tests that use
+// Google Test as a shared library (known as
+// DLL on Windows).
+// GTEST_CREATE_SHARED_LIBRARY
+// - Define to 1 when compiling Google Test itself
+// as a shared library.
+
+// This header defines the following utilities:
+//
+// Macros indicating the current platform (defined to 1 if compiled on
+// the given platform; otherwise undefined):
+// GTEST_OS_AIX - IBM AIX
+// GTEST_OS_CYGWIN - Cygwin
+// GTEST_OS_HPUX - HP-UX
+// GTEST_OS_LINUX - Linux
+// GTEST_OS_LINUX_ANDROID - Google Android
+// GTEST_OS_MAC - Mac OS X
+// GTEST_OS_IOS - iOS
+// GTEST_OS_IOS_SIMULATOR - iOS simulator
+// GTEST_OS_NACL - Google Native Client (NaCl)
+// GTEST_OS_OPENBSD - OpenBSD
+// GTEST_OS_QNX - QNX
+// GTEST_OS_SOLARIS - Sun Solaris
+// GTEST_OS_SYMBIAN - Symbian
+// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile)
+// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop
+// GTEST_OS_WINDOWS_MINGW - MinGW
+// GTEST_OS_WINDOWS_MOBILE - Windows Mobile
+// GTEST_OS_ZOS - z/OS
+//
+// Among the platforms, Cygwin, Linux, Max OS X, and Windows have the
+// most stable support. Since core members of the Google Test project
+// don't have access to other platforms, support for them may be less
+// stable. If you notice any problems on your platform, please notify
+// googletestframework@googlegroups.com (patches for fixing them are
+// even more welcome!).
+//
+// Note that it is possible that none of the GTEST_OS_* macros are defined.
+//
+// Macros indicating available Google Test features (defined to 1 if
+// the corresponding feature is supported; otherwise undefined):
+// GTEST_HAS_COMBINE - the Combine() function (for value-parameterized
+// tests)
+// GTEST_HAS_DEATH_TEST - death tests
+// GTEST_HAS_PARAM_TEST - value-parameterized tests
+// GTEST_HAS_TYPED_TEST - typed tests
+// GTEST_HAS_TYPED_TEST_P - type-parameterized tests
+// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with
+// GTEST_HAS_POSIX_RE (see above) which users can
+// define themselves.
+// GTEST_USES_SIMPLE_RE - our own simple regex is used;
+// the above two are mutually exclusive.
+// GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ().
+//
+// Macros for basic C++ coding:
+// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning.
+// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a
+// variable don't have to be used.
+// GTEST_DISALLOW_ASSIGN_ - disables operator=.
+// GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=.
+// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used.
+//
+// Synchronization:
+// Mutex, MutexLock, ThreadLocal, GetThreadCount()
+// - synchronization primitives.
+// GTEST_IS_THREADSAFE - defined to 1 to indicate that the above
+// synchronization primitives have real implementations
+// and Google Test is thread-safe; or 0 otherwise.
+//
+// Template meta programming:
+// is_pointer - as in TR1; needed on Symbian and IBM XL C/C++ only.
+// IteratorTraits - partial implementation of std::iterator_traits, which
+// is not available in libCstd when compiled with Sun C++.
+//
+// Smart pointers:
+// scoped_ptr - as in TR2.
+//
+// Regular expressions:
+// RE - a simple regular expression class using the POSIX
+// Extended Regular Expression syntax on UNIX-like
+// platforms, or a reduced regular exception syntax on
+// other platforms, including Windows.
+//
+// Logging:
+// GTEST_LOG_() - logs messages at the specified severity level.
+// LogToStderr() - directs all log messages to stderr.
+// FlushInfoLog() - flushes informational log messages.
+//
+// Stdout and stderr capturing:
+// CaptureStdout() - starts capturing stdout.
+// GetCapturedStdout() - stops capturing stdout and returns the captured
+// string.
+// CaptureStderr() - starts capturing stderr.
+// GetCapturedStderr() - stops capturing stderr and returns the captured
+// string.
+//
+// Integer types:
+// TypeWithSize - maps an integer to a int type.
+// Int32, UInt32, Int64, UInt64, TimeInMillis
+// - integers of known sizes.
+// BiggestInt - the biggest signed integer type.
+//
+// Command-line utilities:
+// GTEST_FLAG() - references a flag.
+// GTEST_DECLARE_*() - declares a flag.
+// GTEST_DEFINE_*() - defines a flag.
+// GetInjectableArgvs() - returns the command line as a vector of strings.
+//
+// Environment variable utilities:
+// GetEnv() - gets the value of an environment variable.
+// BoolFromGTestEnv() - parses a bool environment variable.
+// Int32FromGTestEnv() - parses an Int32 environment variable.
+// StringFromGTestEnv() - parses a string environment variable.
+
+#include <ctype.h> // for isspace, etc
+#include <stddef.h> // for ptrdiff_t
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifndef _WIN32_WCE
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif // !_WIN32_WCE
+
+#if defined __APPLE__
+# include <AvailabilityMacros.h>
+# include <TargetConditionals.h>
+#endif
+
+#include <iostream> // NOLINT
+#include <sstream> // NOLINT
+#include <string> // NOLINT
+
+#define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com"
+#define GTEST_FLAG_PREFIX_ "gtest_"
+#define GTEST_FLAG_PREFIX_DASH_ "gtest-"
+#define GTEST_FLAG_PREFIX_UPPER_ "GTEST_"
+#define GTEST_NAME_ "Google Test"
+#define GTEST_PROJECT_URL_ "http://code.google.com/p/googletest/"
+
+// Determines the version of gcc that is used to compile this.
+#ifdef __GNUC__
+// 40302 means version 4.3.2.
+# define GTEST_GCC_VER_ \
+ (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__)
+#endif // __GNUC__
+
+// Determines the platform on which Google Test is compiled.
+#ifdef __CYGWIN__
+# define GTEST_OS_CYGWIN 1
+#elif defined __SYMBIAN32__
+# define GTEST_OS_SYMBIAN 1
+#elif defined _WIN32
+# define GTEST_OS_WINDOWS 1
+# ifdef _WIN32_WCE
+# define GTEST_OS_WINDOWS_MOBILE 1
+# elif defined(__MINGW__) || defined(__MINGW32__)
+# define GTEST_OS_WINDOWS_MINGW 1
+# else
+# define GTEST_OS_WINDOWS_DESKTOP 1
+# endif // _WIN32_WCE
+#elif defined __APPLE__
+# define GTEST_OS_MAC 1
+# if TARGET_OS_IPHONE
+# define GTEST_OS_IOS 1
+# if TARGET_IPHONE_SIMULATOR
+# define GTEST_OS_IOS_SIMULATOR 1
+# endif
+# endif
+#elif defined __linux__
+# define GTEST_OS_LINUX 1
+# if defined __ANDROID__
+# define GTEST_OS_LINUX_ANDROID 1
+# endif
+#elif defined __MVS__
+# define GTEST_OS_ZOS 1
+#elif defined(__sun) && defined(__SVR4)
+# define GTEST_OS_SOLARIS 1
+#elif defined(_AIX)
+# define GTEST_OS_AIX 1
+#elif defined(__hpux)
+# define GTEST_OS_HPUX 1
+#elif defined __native_client__
+# define GTEST_OS_NACL 1
+#elif defined __OpenBSD__
+# define GTEST_OS_OPENBSD 1
+#elif defined __QNX__
+# define GTEST_OS_QNX 1
+#endif // __CYGWIN__
+
+#ifndef GTEST_LANG_CXX11
+// gcc and clang define __GXX_EXPERIMENTAL_CXX0X__ when
+// -std={c,gnu}++{0x,11} is passed. The C++11 standard specifies a
+// value for __cplusplus, and recent versions of clang, gcc, and
+// probably other compilers set that too in C++11 mode.
+# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L
+// Compiling in at least C++11 mode.
+# define GTEST_LANG_CXX11 1
+# else
+# define GTEST_LANG_CXX11 0
+# endif
+#endif
+
+// Brings in definitions for functions used in the testing::internal::posix
+// namespace (read, write, close, chdir, isatty, stat). We do not currently
+// use them on Windows Mobile.
+#if !GTEST_OS_WINDOWS
+// This assumes that non-Windows OSes provide unistd.h. For OSes where this
+// is not the case, we need to include headers that provide the functions
+// mentioned above.
+# include <unistd.h>
+# include <strings.h>
+#elif !GTEST_OS_WINDOWS_MOBILE
+# include <direct.h>
+# include <io.h>
+#endif
+
+#if GTEST_OS_LINUX_ANDROID
+// Used to define __ANDROID_API__ matching the target NDK API level.
+# include <android/api-level.h> // NOLINT
+#endif
+
+// Defines this to true iff Google Test can use POSIX regular expressions.
+#ifndef GTEST_HAS_POSIX_RE
+# if GTEST_OS_LINUX_ANDROID
+// On Android, <regex.h> is only available starting with Gingerbread.
+# define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9)
+# else
+# define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS)
+# endif
+#endif
+
+#if GTEST_HAS_POSIX_RE
+
+// On some platforms, <regex.h> needs someone to define size_t, and
+// won't compile otherwise. We can #include it here as we already
+// included <stdlib.h>, which is guaranteed to define size_t through
+// <stddef.h>.
+# include <regex.h> // NOLINT
+
+# define GTEST_USES_POSIX_RE 1
+
+#elif GTEST_OS_WINDOWS
+
+// <regex.h> is not available on Windows. Use our own simple regex
+// implementation instead.
+# define GTEST_USES_SIMPLE_RE 1
+
+#else
+
+// <regex.h> may not be available on this platform. Use our own
+// simple regex implementation instead.
+# define GTEST_USES_SIMPLE_RE 1
+
+#endif // GTEST_HAS_POSIX_RE
+
+#ifndef GTEST_HAS_EXCEPTIONS
+// The user didn't tell us whether exceptions are enabled, so we need
+// to figure it out.
+# if defined(_MSC_VER) || defined(__BORLANDC__)
+// MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS
+// macro to enable exceptions, so we'll do the same.
+// Assumes that exceptions are enabled by default.
+# ifndef _HAS_EXCEPTIONS
+# define _HAS_EXCEPTIONS 1
+# endif // _HAS_EXCEPTIONS
+# define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS
+# elif defined(__GNUC__) && __EXCEPTIONS
+// gcc defines __EXCEPTIONS to 1 iff exceptions are enabled.
+# define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__SUNPRO_CC)
+// Sun Pro CC supports exceptions. However, there is no compile-time way of
+// detecting whether they are enabled or not. Therefore, we assume that
+// they are enabled unless the user tells us otherwise.
+# define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__IBMCPP__) && __EXCEPTIONS
+// xlC defines __EXCEPTIONS to 1 iff exceptions are enabled.
+# define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__HP_aCC)
+// Exception handling is in effect by default in HP aCC compiler. It has to
+// be turned of by +noeh compiler option if desired.
+# define GTEST_HAS_EXCEPTIONS 1
+# else
+// For other compilers, we assume exceptions are disabled to be
+// conservative.
+# define GTEST_HAS_EXCEPTIONS 0
+# endif // defined(_MSC_VER) || defined(__BORLANDC__)
+#endif // GTEST_HAS_EXCEPTIONS
+
+#if !defined(GTEST_HAS_STD_STRING)
+// Even though we don't use this macro any longer, we keep it in case
+// some clients still depend on it.
+# define GTEST_HAS_STD_STRING 1
+#elif !GTEST_HAS_STD_STRING
+// The user told us that ::std::string isn't available.
+# error "Google Test cannot be used where ::std::string isn't available."
+#endif // !defined(GTEST_HAS_STD_STRING)
+
+#ifndef GTEST_HAS_GLOBAL_STRING
+// The user didn't tell us whether ::string is available, so we need
+// to figure it out.
+
+# define GTEST_HAS_GLOBAL_STRING 0
+
+#endif // GTEST_HAS_GLOBAL_STRING
+
+#ifndef GTEST_HAS_STD_WSTRING
+// The user didn't tell us whether ::std::wstring is available, so we need
+// to figure it out.
+// TODO(wan@google.com): uses autoconf to detect whether ::std::wstring
+// is available.
+
+// Cygwin 1.7 and below doesn't support ::std::wstring.
+// Solaris' libc++ doesn't support it either. Android has
+// no support for it at least as recent as Froyo (2.2).
+# define GTEST_HAS_STD_WSTRING \
+ (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS))
+
+#endif // GTEST_HAS_STD_WSTRING
+
+#ifndef GTEST_HAS_GLOBAL_WSTRING
+// The user didn't tell us whether ::wstring is available, so we need
+// to figure it out.
+# define GTEST_HAS_GLOBAL_WSTRING \
+ (GTEST_HAS_STD_WSTRING && GTEST_HAS_GLOBAL_STRING)
+#endif // GTEST_HAS_GLOBAL_WSTRING
+
+// Determines whether RTTI is available.
+#ifndef GTEST_HAS_RTTI
+// The user didn't tell us whether RTTI is enabled, so we need to
+// figure it out.
+
+# ifdef _MSC_VER
+
+# ifdef _CPPRTTI // MSVC defines this macro iff RTTI is enabled.
+# define GTEST_HAS_RTTI 1
+# else
+# define GTEST_HAS_RTTI 0
+# endif
+
+// Starting with version 4.3.2, gcc defines __GXX_RTTI iff RTTI is enabled.
+# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302)
+
+# ifdef __GXX_RTTI
+// When building against STLport with the Android NDK and with
+// -frtti -fno-exceptions, the build fails at link time with undefined
+// references to __cxa_bad_typeid. Note sure if STL or toolchain bug,
+// so disable RTTI when detected.
+# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) && \
+ !defined(__EXCEPTIONS)
+# define GTEST_HAS_RTTI 0
+# else
+# define GTEST_HAS_RTTI 1
+# endif // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS
+# else
+# define GTEST_HAS_RTTI 0
+# endif // __GXX_RTTI
+
+// Clang defines __GXX_RTTI starting with version 3.0, but its manual recommends
+// using has_feature instead. has_feature(cxx_rtti) is supported since 2.7, the
+// first version with C++ support.
+# elif defined(__clang__)
+
+# define GTEST_HAS_RTTI __has_feature(cxx_rtti)
+
+// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if
+// both the typeid and dynamic_cast features are present.
+# elif defined(__IBMCPP__) && (__IBMCPP__ >= 900)
+
+# ifdef __RTTI_ALL__
+# define GTEST_HAS_RTTI 1
+# else
+# define GTEST_HAS_RTTI 0
+# endif
+
+# else
+
+// For all other compilers, we assume RTTI is enabled.
+# define GTEST_HAS_RTTI 1
+
+# endif // _MSC_VER
+
+#endif // GTEST_HAS_RTTI
+
+// It's this header's responsibility to #include <typeinfo> when RTTI
+// is enabled.
+#if GTEST_HAS_RTTI
+# include <typeinfo>
+#endif
+
+// Determines whether Google Test can use the pthreads library.
+#ifndef GTEST_HAS_PTHREAD
+// The user didn't tell us explicitly, so we assume pthreads support is
+// available on Linux and Mac.
+//
+// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0
+// to your compiler flags.
+# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX \
+ || GTEST_OS_QNX)
+#endif // GTEST_HAS_PTHREAD
+
+#if GTEST_HAS_PTHREAD
+// gtest-port.h guarantees to #include <pthread.h> when GTEST_HAS_PTHREAD is
+// true.
+# include <pthread.h> // NOLINT
+
+// For timespec and nanosleep, used below.
+# include <time.h> // NOLINT
+#endif
+
+// Determines whether Google Test can use tr1/tuple. You can define
+// this macro to 0 to prevent Google Test from using tuple (any
+// feature depending on tuple with be disabled in this mode).
+#ifndef GTEST_HAS_TR1_TUPLE
+# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR)
+// STLport, provided with the Android NDK, has neither <tr1/tuple> or <tuple>.
+# define GTEST_HAS_TR1_TUPLE 0
+# else
+// The user didn't tell us not to do it, so we assume it's OK.
+# define GTEST_HAS_TR1_TUPLE 1
+# endif
+#endif // GTEST_HAS_TR1_TUPLE
+
+// Determines whether Google Test's own tr1 tuple implementation
+// should be used.
+#ifndef GTEST_USE_OWN_TR1_TUPLE
+// The user didn't tell us, so we need to figure it out.
+
+// We use our own TR1 tuple if we aren't sure the user has an
+// implementation of it already. At this time, libstdc++ 4.0.0+ and
+// MSVC 2010 are the only mainstream standard libraries that come
+// with a TR1 tuple implementation. NVIDIA's CUDA NVCC compiler
+// pretends to be GCC by defining __GNUC__ and friends, but cannot
+// compile GCC's tuple implementation. MSVC 2008 (9.0) provides TR1
+// tuple in a 323 MB Feature Pack download, which we cannot assume the
+// user has. QNX's QCC compiler is a modified GCC but it doesn't
+// support TR1 tuple. libc++ only provides std::tuple, in C++11 mode,
+// and it can be used with some compilers that define __GNUC__.
+# if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000) \
+ && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) || _MSC_VER >= 1600
+# define GTEST_ENV_HAS_TR1_TUPLE_ 1
+# endif
+
+// C++11 specifies that <tuple> provides std::tuple. Use that if gtest is used
+// in C++11 mode and libstdc++ isn't very old (binaries targeting OS X 10.6
+// can build with clang but need to use gcc4.2's libstdc++).
+# if GTEST_LANG_CXX11 && (!defined(__GLIBCXX__) || __GLIBCXX__ > 20110325)
+# define GTEST_ENV_HAS_STD_TUPLE_ 1
+# endif
+
+# if GTEST_ENV_HAS_TR1_TUPLE_ || GTEST_ENV_HAS_STD_TUPLE_
+# define GTEST_USE_OWN_TR1_TUPLE 0
+# else
+# define GTEST_USE_OWN_TR1_TUPLE 1
+# endif
+
+#endif // GTEST_USE_OWN_TR1_TUPLE
+
+// To avoid conditional compilation everywhere, we make it
+// gtest-port.h's responsibility to #include the header implementing
+// tr1/tuple.
+#if GTEST_HAS_TR1_TUPLE
+
+# if GTEST_USE_OWN_TR1_TUPLE
+# include "gtest/internal/gtest-tuple.h"
+# elif GTEST_ENV_HAS_STD_TUPLE_
+# include <tuple>
+// C++11 puts its tuple into the ::std namespace rather than
+// ::std::tr1. gtest expects tuple to live in ::std::tr1, so put it there.
+// This causes undefined behavior, but supported compilers react in
+// the way we intend.
+namespace std {
+namespace tr1 {
+using ::std::get;
+using ::std::make_tuple;
+using ::std::tuple;
+using ::std::tuple_element;
+using ::std::tuple_size;
+}
+}
+
+# elif GTEST_OS_SYMBIAN
+
+// On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to
+// use STLport's tuple implementation, which unfortunately doesn't
+// work as the copy of STLport distributed with Symbian is incomplete.
+// By making sure BOOST_HAS_TR1_TUPLE is undefined, we force Boost to
+// use its own tuple implementation.
+# ifdef BOOST_HAS_TR1_TUPLE
+# undef BOOST_HAS_TR1_TUPLE
+# endif // BOOST_HAS_TR1_TUPLE
+
+// This prevents <boost/tr1/detail/config.hpp>, which defines
+// BOOST_HAS_TR1_TUPLE, from being #included by Boost's <tuple>.
+# define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED
+# include <tuple>
+
+# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000)
+// GCC 4.0+ implements tr1/tuple in the <tr1/tuple> header. This does
+// not conform to the TR1 spec, which requires the header to be <tuple>.
+
+# if !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302
+// Until version 4.3.2, gcc has a bug that causes <tr1/functional>,
+// which is #included by <tr1/tuple>, to not compile when RTTI is
+// disabled. _TR1_FUNCTIONAL is the header guard for
+// <tr1/functional>. Hence the following #define is a hack to prevent
+// <tr1/functional> from being included.
+# define _TR1_FUNCTIONAL 1
+# include <tr1/tuple>
+# undef _TR1_FUNCTIONAL // Allows the user to #include
+ // <tr1/functional> if he chooses to.
+# else
+# include <tr1/tuple> // NOLINT
+# endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302
+
+# else
+// If the compiler is not GCC 4.0+, we assume the user is using a
+// spec-conforming TR1 implementation.
+# include <tuple> // NOLINT
+# endif // GTEST_USE_OWN_TR1_TUPLE
+
+#endif // GTEST_HAS_TR1_TUPLE
+
+// Determines whether clone(2) is supported.
+// Usually it will only be available on Linux, excluding
+// Linux on the Itanium architecture.
+// Also see http://linux.die.net/man/2/clone.
+#ifndef GTEST_HAS_CLONE
+// The user didn't tell us, so we need to figure it out.
+
+# if GTEST_OS_LINUX && !defined(__ia64__)
+# if GTEST_OS_LINUX_ANDROID
+// On Android, clone() is only available on ARM starting with Gingerbread.
+# if defined(__arm__) && __ANDROID_API__ >= 9
+# define GTEST_HAS_CLONE 1
+# else
+# define GTEST_HAS_CLONE 0
+# endif
+# else
+# define GTEST_HAS_CLONE 1
+# endif
+# else
+# define GTEST_HAS_CLONE 0
+# endif // GTEST_OS_LINUX && !defined(__ia64__)
+
+#endif // GTEST_HAS_CLONE
+
+// Determines whether to support stream redirection. This is used to test
+// output correctness and to implement death tests.
+#ifndef GTEST_HAS_STREAM_REDIRECTION
+// By default, we assume that stream redirection is supported on all
+// platforms except known mobile ones.
+# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN
+# define GTEST_HAS_STREAM_REDIRECTION 0
+# else
+# define GTEST_HAS_STREAM_REDIRECTION 1
+# endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN
+#endif // GTEST_HAS_STREAM_REDIRECTION
+
+// Determines whether to support death tests.
+// Google Test does not support death tests for VC 7.1 and earlier as
+// abort() in a VC 7.1 application compiled as GUI in debug config
+// pops up a dialog window that cannot be suppressed programmatically.
+#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \
+ (GTEST_OS_MAC && !GTEST_OS_IOS) || GTEST_OS_IOS_SIMULATOR || \
+ (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \
+ GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX || \
+ GTEST_OS_OPENBSD || GTEST_OS_QNX)
+# define GTEST_HAS_DEATH_TEST 1
+# include <vector> // NOLINT
+#endif
+
+// We don't support MSVC 7.1 with exceptions disabled now. Therefore
+// all the compilers we care about are adequate for supporting
+// value-parameterized tests.
+#define GTEST_HAS_PARAM_TEST 1
+
+// Determines whether to support type-driven tests.
+
+// Typed tests need <typeinfo> and variadic macros, which GCC, VC++ 8.0,
+// Sun Pro CC, IBM Visual Age, and HP aCC support.
+#if defined(__GNUC__) || (_MSC_VER >= 1400) || defined(__SUNPRO_CC) || \
+ defined(__IBMCPP__) || defined(__HP_aCC)
+# define GTEST_HAS_TYPED_TEST 1
+# define GTEST_HAS_TYPED_TEST_P 1
+#endif
+
+// Determines whether to support Combine(). This only makes sense when
+// value-parameterized tests are enabled. The implementation doesn't
+// work on Sun Studio since it doesn't understand templated conversion
+// operators.
+#if GTEST_HAS_PARAM_TEST && GTEST_HAS_TR1_TUPLE && !defined(__SUNPRO_CC)
+# define GTEST_HAS_COMBINE 1
+#endif
+
+// Determines whether the system compiler uses UTF-16 for encoding wide strings.
+#define GTEST_WIDE_STRING_USES_UTF16_ \
+ (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_SYMBIAN || GTEST_OS_AIX)
+
+// Determines whether test results can be streamed to a socket.
+#if GTEST_OS_LINUX
+# define GTEST_CAN_STREAM_RESULTS_ 1
+#endif
+
+// Defines some utility macros.
+
+// The GNU compiler emits a warning if nested "if" statements are followed by
+// an "else" statement and braces are not used to explicitly disambiguate the
+// "else" binding. This leads to problems with code like:
+//
+// if (gate)
+// ASSERT_*(condition) << "Some message";
+//
+// The "switch (0) case 0:" idiom is used to suppress this.
+#ifdef __INTEL_COMPILER
+# define GTEST_AMBIGUOUS_ELSE_BLOCKER_
+#else
+# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: default: // NOLINT
+#endif
+
+// Use this annotation at the end of a struct/class definition to
+// prevent the compiler from optimizing away instances that are never
+// used. This is useful when all interesting logic happens inside the
+// c'tor and / or d'tor. Example:
+//
+// struct Foo {
+// Foo() { ... }
+// } GTEST_ATTRIBUTE_UNUSED_;
+//
+// Also use it after a variable or parameter declaration to tell the
+// compiler the variable/parameter does not have to be used.
+#if defined(__GNUC__) && !defined(COMPILER_ICC)
+# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused))
+#else
+# define GTEST_ATTRIBUTE_UNUSED_
+#endif
+
+// A macro to disallow operator=
+// This should be used in the private: declarations for a class.
+#define GTEST_DISALLOW_ASSIGN_(type)\
+ void operator=(type const &)
+
+// A macro to disallow copy constructor and operator=
+// This should be used in the private: declarations for a class.
+#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type)\
+ type(type const &);\
+ GTEST_DISALLOW_ASSIGN_(type)
+
+// Tell the compiler to warn about unused return values for functions declared
+// with this macro. The macro should be used on function declarations
+// following the argument list:
+//
+// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_;
+#if defined(__GNUC__) && (GTEST_GCC_VER_ >= 30400) && !defined(COMPILER_ICC)
+# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result))
+#else
+# define GTEST_MUST_USE_RESULT_
+#endif // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC
+
+// Determine whether the compiler supports Microsoft's Structured Exception
+// Handling. This is supported by several Windows compilers but generally
+// does not exist on any other system.
+#ifndef GTEST_HAS_SEH
+// The user didn't tell us, so we need to figure it out.
+
+# if defined(_MSC_VER) || defined(__BORLANDC__)
+// These two compilers are known to support SEH.
+# define GTEST_HAS_SEH 1
+# else
+// Assume no SEH.
+# define GTEST_HAS_SEH 0
+# endif
+
+#endif // GTEST_HAS_SEH
+
+#ifdef _MSC_VER
+
+# if GTEST_LINKED_AS_SHARED_LIBRARY
+# define GTEST_API_ __declspec(dllimport)
+# elif GTEST_CREATE_SHARED_LIBRARY
+# define GTEST_API_ __declspec(dllexport)
+# endif
+
+#endif // _MSC_VER
+
+#ifndef GTEST_API_
+# define GTEST_API_
+#endif
+
+#ifdef __GNUC__
+// Ask the compiler to never inline a given function.
+# define GTEST_NO_INLINE_ __attribute__((noinline))
+#else
+# define GTEST_NO_INLINE_
+#endif
+
+// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project.
+#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION)
+# define GTEST_HAS_CXXABI_H_ 1
+#else
+# define GTEST_HAS_CXXABI_H_ 0
+#endif
+
+namespace testing {
+
+class Message;
+
+namespace internal {
+
+// A secret type that Google Test users don't know about. It has no
+// definition on purpose. Therefore it's impossible to create a
+// Secret object, which is what we want.
+class Secret;
+
+// The GTEST_COMPILE_ASSERT_ macro can be used to verify that a compile time
+// expression is true. For example, you could use it to verify the
+// size of a static array:
+//
+// GTEST_COMPILE_ASSERT_(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES,
+// content_type_names_incorrect_size);
+//
+// or to make sure a struct is smaller than a certain size:
+//
+// GTEST_COMPILE_ASSERT_(sizeof(foo) < 128, foo_too_large);
+//
+// The second argument to the macro is the name of the variable. If
+// the expression is false, most compilers will issue a warning/error
+// containing the name of the variable.
+
+template <bool>
+struct CompileAssert {
+};
+
+#define GTEST_COMPILE_ASSERT_(expr, msg) \
+ typedef ::testing::internal::CompileAssert<(static_cast<bool>(expr))> \
+ msg[static_cast<bool>(expr) ? 1 : -1] GTEST_ATTRIBUTE_UNUSED_
+
+// Implementation details of GTEST_COMPILE_ASSERT_:
+//
+// - GTEST_COMPILE_ASSERT_ works by defining an array type that has -1
+// elements (and thus is invalid) when the expression is false.
+//
+// - The simpler definition
+//
+// #define GTEST_COMPILE_ASSERT_(expr, msg) typedef char msg[(expr) ? 1 : -1]
+//
+// does not work, as gcc supports variable-length arrays whose sizes
+// are determined at run-time (this is gcc's extension and not part
+// of the C++ standard). As a result, gcc fails to reject the
+// following code with the simple definition:
+//
+// int foo;
+// GTEST_COMPILE_ASSERT_(foo, msg); // not supposed to compile as foo is
+// // not a compile-time constant.
+//
+// - By using the type CompileAssert<(bool(expr))>, we ensures that
+// expr is a compile-time constant. (Template arguments must be
+// determined at compile-time.)
+//
+// - The outter parentheses in CompileAssert<(bool(expr))> are necessary
+// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written
+//
+// CompileAssert<bool(expr)>
+//
+// instead, these compilers will refuse to compile
+//
+// GTEST_COMPILE_ASSERT_(5 > 0, some_message);
+//
+// (They seem to think the ">" in "5 > 0" marks the end of the
+// template argument list.)
+//
+// - The array size is (bool(expr) ? 1 : -1), instead of simply
+//
+// ((expr) ? 1 : -1).
+//
+// This is to avoid running into a bug in MS VC 7.1, which
+// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
+
+// StaticAssertTypeEqHelper is used by StaticAssertTypeEq defined in gtest.h.
+//
+// This template is declared, but intentionally undefined.
+template <typename T1, typename T2>
+struct StaticAssertTypeEqHelper;
+
+template <typename T>
+struct StaticAssertTypeEqHelper<T, T> {};
+
+#if GTEST_HAS_GLOBAL_STRING
+typedef ::string string;
+#else
+typedef ::std::string string;
+#endif // GTEST_HAS_GLOBAL_STRING
+
+#if GTEST_HAS_GLOBAL_WSTRING
+typedef ::wstring wstring;
+#elif GTEST_HAS_STD_WSTRING
+typedef ::std::wstring wstring;
+#endif // GTEST_HAS_GLOBAL_WSTRING
+
+// A helper for suppressing warnings on constant condition. It just
+// returns 'condition'.
+GTEST_API_ bool IsTrue(bool condition);
+
+// Defines scoped_ptr.
+
+// This implementation of scoped_ptr is PARTIAL - it only contains
+// enough stuff to satisfy Google Test's need.
+template <typename T>
+class scoped_ptr {
+ public:
+ typedef T element_type;
+
+ explicit scoped_ptr(T* p = NULL) : ptr_(p) {}
+ ~scoped_ptr() { reset(); }
+
+ T& operator*() const { return *ptr_; }
+ T* operator->() const { return ptr_; }
+ T* get() const { return ptr_; }
+
+ T* release() {
+ T* const ptr = ptr_;
+ ptr_ = NULL;
+ return ptr;
+ }
+
+ void reset(T* p = NULL) {
+ if (p != ptr_) {
+ if (IsTrue(sizeof(T) > 0)) { // Makes sure T is a complete type.
+ delete ptr_;
+ }
+ ptr_ = p;
+ }
+ }
+
+ private:
+ T* ptr_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(scoped_ptr);
+};
+
+// Defines RE.
+
+// A simple C++ wrapper for <regex.h>. It uses the POSIX Extended
+// Regular Expression syntax.
+class GTEST_API_ RE {
+ public:
+ // A copy constructor is required by the Standard to initialize object
+ // references from r-values.
+ RE(const RE& other) { Init(other.pattern()); }
+
+ // Constructs an RE from a string.
+ RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT
+
+#if GTEST_HAS_GLOBAL_STRING
+
+ RE(const ::string& regex) { Init(regex.c_str()); } // NOLINT
+
+#endif // GTEST_HAS_GLOBAL_STRING
+
+ RE(const char* regex) { Init(regex); } // NOLINT
+ ~RE();
+
+ // Returns the string representation of the regex.
+ const char* pattern() const { return pattern_; }
+
+ // FullMatch(str, re) returns true iff regular expression re matches
+ // the entire str.
+ // PartialMatch(str, re) returns true iff regular expression re
+ // matches a substring of str (including str itself).
+ //
+ // TODO(wan@google.com): make FullMatch() and PartialMatch() work
+ // when str contains NUL characters.
+ static bool FullMatch(const ::std::string& str, const RE& re) {
+ return FullMatch(str.c_str(), re);
+ }
+ static bool PartialMatch(const ::std::string& str, const RE& re) {
+ return PartialMatch(str.c_str(), re);
+ }
+
+#if GTEST_HAS_GLOBAL_STRING
+
+ static bool FullMatch(const ::string& str, const RE& re) {
+ return FullMatch(str.c_str(), re);
+ }
+ static bool PartialMatch(const ::string& str, const RE& re) {
+ return PartialMatch(str.c_str(), re);
+ }
+
+#endif // GTEST_HAS_GLOBAL_STRING
+
+ static bool FullMatch(const char* str, const RE& re);
+ static bool PartialMatch(const char* str, const RE& re);
+
+ private:
+ void Init(const char* regex);
+
+ // We use a const char* instead of an std::string, as Google Test used to be
+ // used where std::string is not available. TODO(wan@google.com): change to
+ // std::string.
+ const char* pattern_;
+ bool is_valid_;
+
+#if GTEST_USES_POSIX_RE
+
+ regex_t full_regex_; // For FullMatch().
+ regex_t partial_regex_; // For PartialMatch().
+
+#else // GTEST_USES_SIMPLE_RE
+
+ const char* full_pattern_; // For FullMatch();
+
+#endif
+
+ GTEST_DISALLOW_ASSIGN_(RE);
+};
+
+// Formats a source file path and a line number as they would appear
+// in an error message from the compiler used to compile this code.
+GTEST_API_ ::std::string FormatFileLocation(const char* file, int line);
+
+// Formats a file location for compiler-independent XML output.
+// Although this function is not platform dependent, we put it next to
+// FormatFileLocation in order to contrast the two functions.
+GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file,
+ int line);
+
+// Defines logging utilities:
+// GTEST_LOG_(severity) - logs messages at the specified severity level. The
+// message itself is streamed into the macro.
+// LogToStderr() - directs all log messages to stderr.
+// FlushInfoLog() - flushes informational log messages.
+
+enum GTestLogSeverity {
+ GTEST_INFO,
+ GTEST_WARNING,
+ GTEST_ERROR,
+ GTEST_FATAL
+};
+
+// Formats log entry severity, provides a stream object for streaming the
+// log message, and terminates the message with a newline when going out of
+// scope.
+class GTEST_API_ GTestLog {
+ public:
+ GTestLog(GTestLogSeverity severity, const char* file, int line);
+
+ // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program.
+ ~GTestLog();
+
+ ::std::ostream& GetStream() { return ::std::cerr; }
+
+ private:
+ const GTestLogSeverity severity_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestLog);
+};
+
+#define GTEST_LOG_(severity) \
+ ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \
+ __FILE__, __LINE__).GetStream()
+
+inline void LogToStderr() {}
+inline void FlushInfoLog() { fflush(NULL); }
+
+// INTERNAL IMPLEMENTATION - DO NOT USE.
+//
+// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition
+// is not satisfied.
+// Synopsys:
+// GTEST_CHECK_(boolean_condition);
+// or
+// GTEST_CHECK_(boolean_condition) << "Additional message";
+//
+// This checks the condition and if the condition is not satisfied
+// it prints message about the condition violation, including the
+// condition itself, plus additional message streamed into it, if any,
+// and then it aborts the program. It aborts the program irrespective of
+// whether it is built in the debug mode or not.
+#define GTEST_CHECK_(condition) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::testing::internal::IsTrue(condition)) \
+ ; \
+ else \
+ GTEST_LOG_(FATAL) << "Condition " #condition " failed. "
+
+// An all-mode assert to verify that the given POSIX-style function
+// call returns 0 (indicating success). Known limitation: this
+// doesn't expand to a balanced 'if' statement, so enclose the macro
+// in {} if you need to use it as the only statement in an 'if'
+// branch.
+#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \
+ if (const int gtest_error = (posix_call)) \
+ GTEST_LOG_(FATAL) << #posix_call << "failed with error " \
+ << gtest_error
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Use ImplicitCast_ as a safe version of static_cast for upcasting in
+// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a
+// const Foo*). When you use ImplicitCast_, the compiler checks that
+// the cast is safe. Such explicit ImplicitCast_s are necessary in
+// surprisingly many situations where C++ demands an exact type match
+// instead of an argument type convertable to a target type.
+//
+// The syntax for using ImplicitCast_ is the same as for static_cast:
+//
+// ImplicitCast_<ToType>(expr)
+//
+// ImplicitCast_ would have been part of the C++ standard library,
+// but the proposal was submitted too late. It will probably make
+// its way into the language in the future.
+//
+// This relatively ugly name is intentional. It prevents clashes with
+// similar functions users may have (e.g., implicit_cast). The internal
+// namespace alone is not enough because the function can be found by ADL.
+template<typename To>
+inline To ImplicitCast_(To x) { return x; }
+
+// When you upcast (that is, cast a pointer from type Foo to type
+// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts
+// always succeed. When you downcast (that is, cast a pointer from
+// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because
+// how do you know the pointer is really of type SubclassOfFoo? It
+// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus,
+// when you downcast, you should use this macro. In debug mode, we
+// use dynamic_cast<> to double-check the downcast is legal (we die
+// if it's not). In normal mode, we do the efficient static_cast<>
+// instead. Thus, it's important to test in debug mode to make sure
+// the cast is legal!
+// This is the only place in the code we should use dynamic_cast<>.
+// In particular, you SHOULDN'T be using dynamic_cast<> in order to
+// do RTTI (eg code like this:
+// if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo);
+// if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo);
+// You should design the code some other way not to need this.
+//
+// This relatively ugly name is intentional. It prevents clashes with
+// similar functions users may have (e.g., down_cast). The internal
+// namespace alone is not enough because the function can be found by ADL.
+template<typename To, typename From> // use like this: DownCast_<T*>(foo);
+inline To DownCast_(From* f) { // so we only accept pointers
+ // Ensures that To is a sub-type of From *. This test is here only
+ // for compile-time type checking, and has no overhead in an
+ // optimized build at run-time, as it will be optimized away
+ // completely.
+ if (false) {
+ const To to = NULL;
+ ::testing::internal::ImplicitCast_<From*>(to);
+ }
+
+#if GTEST_HAS_RTTI
+ // RTTI: debug mode only!
+ GTEST_CHECK_(f == NULL || dynamic_cast<To>(f) != NULL);
+#endif
+ return static_cast<To>(f);
+}
+
+// Downcasts the pointer of type Base to Derived.
+// Derived must be a subclass of Base. The parameter MUST
+// point to a class of type Derived, not any subclass of it.
+// When RTTI is available, the function performs a runtime
+// check to enforce this.
+template <class Derived, class Base>
+Derived* CheckedDowncastToActualType(Base* base) {
+#if GTEST_HAS_RTTI
+ GTEST_CHECK_(typeid(*base) == typeid(Derived));
+ return dynamic_cast<Derived*>(base); // NOLINT
+#else
+ return static_cast<Derived*>(base); // Poor man's downcast.
+#endif
+}
+
+#if GTEST_HAS_STREAM_REDIRECTION
+
+// Defines the stderr capturer:
+// CaptureStdout - starts capturing stdout.
+// GetCapturedStdout - stops capturing stdout and returns the captured string.
+// CaptureStderr - starts capturing stderr.
+// GetCapturedStderr - stops capturing stderr and returns the captured string.
+//
+GTEST_API_ void CaptureStdout();
+GTEST_API_ std::string GetCapturedStdout();
+GTEST_API_ void CaptureStderr();
+GTEST_API_ std::string GetCapturedStderr();
+
+#endif // GTEST_HAS_STREAM_REDIRECTION
+
+
+#if GTEST_HAS_DEATH_TEST
+
+const ::std::vector<testing::internal::string>& GetInjectableArgvs();
+void SetInjectableArgvs(const ::std::vector<testing::internal::string>*
+ new_argvs);
+
+// A copy of all command line arguments. Set by InitGoogleTest().
+extern ::std::vector<testing::internal::string> g_argvs;
+
+#endif // GTEST_HAS_DEATH_TEST
+
+// Defines synchronization primitives.
+
+#if GTEST_HAS_PTHREAD
+
+// Sleeps for (roughly) n milli-seconds. This function is only for
+// testing Google Test's own constructs. Don't use it in user tests,
+// either directly or indirectly.
+inline void SleepMilliseconds(int n) {
+ const timespec time = {
+ 0, // 0 seconds.
+ n * 1000L * 1000L, // And n ms.
+ };
+ nanosleep(&time, NULL);
+}
+
+// Allows a controller thread to pause execution of newly created
+// threads until notified. Instances of this class must be created
+// and destroyed in the controller thread.
+//
+// This class is only for testing Google Test's own constructs. Do not
+// use it in user tests, either directly or indirectly.
+class Notification {
+ public:
+ Notification() : notified_(false) {
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL));
+ }
+ ~Notification() {
+ pthread_mutex_destroy(&mutex_);
+ }
+
+ // Notifies all threads created with this notification to start. Must
+ // be called from the controller thread.
+ void Notify() {
+ pthread_mutex_lock(&mutex_);
+ notified_ = true;
+ pthread_mutex_unlock(&mutex_);
+ }
+
+ // Blocks until the controller thread notifies. Must be called from a test
+ // thread.
+ void WaitForNotification() {
+ for (;;) {
+ pthread_mutex_lock(&mutex_);
+ const bool notified = notified_;
+ pthread_mutex_unlock(&mutex_);
+ if (notified)
+ break;
+ SleepMilliseconds(10);
+ }
+ }
+
+ private:
+ pthread_mutex_t mutex_;
+ bool notified_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification);
+};
+
+// As a C-function, ThreadFuncWithCLinkage cannot be templated itself.
+// Consequently, it cannot select a correct instantiation of ThreadWithParam
+// in order to call its Run(). Introducing ThreadWithParamBase as a
+// non-templated base class for ThreadWithParam allows us to bypass this
+// problem.
+class ThreadWithParamBase {
+ public:
+ virtual ~ThreadWithParamBase() {}
+ virtual void Run() = 0;
+};
+
+// pthread_create() accepts a pointer to a function type with the C linkage.
+// According to the Standard (7.5/1), function types with different linkages
+// are different even if they are otherwise identical. Some compilers (for
+// example, SunStudio) treat them as different types. Since class methods
+// cannot be defined with C-linkage we need to define a free C-function to
+// pass into pthread_create().
+extern "C" inline void* ThreadFuncWithCLinkage(void* thread) {
+ static_cast<ThreadWithParamBase*>(thread)->Run();
+ return NULL;
+}
+
+// Helper class for testing Google Test's multi-threading constructs.
+// To use it, write:
+//
+// void ThreadFunc(int param) { /* Do things with param */ }
+// Notification thread_can_start;
+// ...
+// // The thread_can_start parameter is optional; you can supply NULL.
+// ThreadWithParam<int> thread(&ThreadFunc, 5, &thread_can_start);
+// thread_can_start.Notify();
+//
+// These classes are only for testing Google Test's own constructs. Do
+// not use them in user tests, either directly or indirectly.
+template <typename T>
+class ThreadWithParam : public ThreadWithParamBase {
+ public:
+ typedef void (*UserThreadFunc)(T);
+
+ ThreadWithParam(
+ UserThreadFunc func, T param, Notification* thread_can_start)
+ : func_(func),
+ param_(param),
+ thread_can_start_(thread_can_start),
+ finished_(false) {
+ ThreadWithParamBase* const base = this;
+ // The thread can be created only after all fields except thread_
+ // have been initialized.
+ GTEST_CHECK_POSIX_SUCCESS_(
+ pthread_create(&thread_, 0, &ThreadFuncWithCLinkage, base));
+ }
+ ~ThreadWithParam() { Join(); }
+
+ void Join() {
+ if (!finished_) {
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, 0));
+ finished_ = true;
+ }
+ }
+
+ virtual void Run() {
+ if (thread_can_start_ != NULL)
+ thread_can_start_->WaitForNotification();
+ func_(param_);
+ }
+
+ private:
+ const UserThreadFunc func_; // User-supplied thread function.
+ const T param_; // User-supplied parameter to the thread function.
+ // When non-NULL, used to block execution until the controller thread
+ // notifies.
+ Notification* const thread_can_start_;
+ bool finished_; // true iff we know that the thread function has finished.
+ pthread_t thread_; // The native thread object.
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam);
+};
+
+// MutexBase and Mutex implement mutex on pthreads-based platforms. They
+// are used in conjunction with class MutexLock:
+//
+// Mutex mutex;
+// ...
+// MutexLock lock(&mutex); // Acquires the mutex and releases it at the end
+// // of the current scope.
+//
+// MutexBase implements behavior for both statically and dynamically
+// allocated mutexes. Do not use MutexBase directly. Instead, write
+// the following to define a static mutex:
+//
+// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex);
+//
+// You can forward declare a static mutex like this:
+//
+// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex);
+//
+// To create a dynamic mutex, just define an object of type Mutex.
+class MutexBase {
+ public:
+ // Acquires this mutex.
+ void Lock() {
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_));
+ owner_ = pthread_self();
+ has_owner_ = true;
+ }
+
+ // Releases this mutex.
+ void Unlock() {
+ // Since the lock is being released the owner_ field should no longer be
+ // considered valid. We don't protect writing to has_owner_ here, as it's
+ // the caller's responsibility to ensure that the current thread holds the
+ // mutex when this is called.
+ has_owner_ = false;
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_));
+ }
+
+ // Does nothing if the current thread holds the mutex. Otherwise, crashes
+ // with high probability.
+ void AssertHeld() const {
+ GTEST_CHECK_(has_owner_ && pthread_equal(owner_, pthread_self()))
+ << "The current thread is not holding the mutex @" << this;
+ }
+
+ // A static mutex may be used before main() is entered. It may even
+ // be used before the dynamic initialization stage. Therefore we
+ // must be able to initialize a static mutex object at link time.
+ // This means MutexBase has to be a POD and its member variables
+ // have to be public.
+ public:
+ pthread_mutex_t mutex_; // The underlying pthread mutex.
+ // has_owner_ indicates whether the owner_ field below contains a valid thread
+ // ID and is therefore safe to inspect (e.g., to use in pthread_equal()). All
+ // accesses to the owner_ field should be protected by a check of this field.
+ // An alternative might be to memset() owner_ to all zeros, but there's no
+ // guarantee that a zero'd pthread_t is necessarily invalid or even different
+ // from pthread_self().
+ bool has_owner_;
+ pthread_t owner_; // The thread holding the mutex.
+};
+
+// Forward-declares a static mutex.
+# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \
+ extern ::testing::internal::MutexBase mutex
+
+// Defines and statically (i.e. at link time) initializes a static mutex.
+// The initialization list here does not explicitly initialize each field,
+// instead relying on default initialization for the unspecified fields. In
+// particular, the owner_ field (a pthread_t) is not explicitly initialized.
+// This allows initialization to work whether pthread_t is a scalar or struct.
+// The flag -Wmissing-field-initializers must not be specified for this to work.
+# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \
+ ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false }
+
+// The Mutex class can only be used for mutexes created at runtime. It
+// shares its API with MutexBase otherwise.
+class Mutex : public MutexBase {
+ public:
+ Mutex() {
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL));
+ has_owner_ = false;
+ }
+ ~Mutex() {
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_));
+ }
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex);
+};
+
+// We cannot name this class MutexLock as the ctor declaration would
+// conflict with a macro named MutexLock, which is defined on some
+// platforms. Hence the typedef trick below.
+class GTestMutexLock {
+ public:
+ explicit GTestMutexLock(MutexBase* mutex)
+ : mutex_(mutex) { mutex_->Lock(); }
+
+ ~GTestMutexLock() { mutex_->Unlock(); }
+
+ private:
+ MutexBase* const mutex_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock);
+};
+
+typedef GTestMutexLock MutexLock;
+
+// Helpers for ThreadLocal.
+
+// pthread_key_create() requires DeleteThreadLocalValue() to have
+// C-linkage. Therefore it cannot be templatized to access
+// ThreadLocal<T>. Hence the need for class
+// ThreadLocalValueHolderBase.
+class ThreadLocalValueHolderBase {
+ public:
+ virtual ~ThreadLocalValueHolderBase() {}
+};
+
+// Called by pthread to delete thread-local data stored by
+// pthread_setspecific().
+extern "C" inline void DeleteThreadLocalValue(void* value_holder) {
+ delete static_cast<ThreadLocalValueHolderBase*>(value_holder);
+}
+
+// Implements thread-local storage on pthreads-based systems.
+//
+// // Thread 1
+// ThreadLocal<int> tl(100); // 100 is the default value for each thread.
+//
+// // Thread 2
+// tl.set(150); // Changes the value for thread 2 only.
+// EXPECT_EQ(150, tl.get());
+//
+// // Thread 1
+// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value.
+// tl.set(200);
+// EXPECT_EQ(200, tl.get());
+//
+// The template type argument T must have a public copy constructor.
+// In addition, the default ThreadLocal constructor requires T to have
+// a public default constructor.
+//
+// An object managed for a thread by a ThreadLocal instance is deleted
+// when the thread exits. Or, if the ThreadLocal instance dies in
+// that thread, when the ThreadLocal dies. It's the user's
+// responsibility to ensure that all other threads using a ThreadLocal
+// have exited when it dies, or the per-thread objects for those
+// threads will not be deleted.
+//
+// Google Test only uses global ThreadLocal objects. That means they
+// will die after main() has returned. Therefore, no per-thread
+// object managed by Google Test will be leaked as long as all threads
+// using Google Test have exited when main() returns.
+template <typename T>
+class ThreadLocal {
+ public:
+ ThreadLocal() : key_(CreateKey()),
+ default_() {}
+ explicit ThreadLocal(const T& value) : key_(CreateKey()),
+ default_(value) {}
+
+ ~ThreadLocal() {
+ // Destroys the managed object for the current thread, if any.
+ DeleteThreadLocalValue(pthread_getspecific(key_));
+
+ // Releases resources associated with the key. This will *not*
+ // delete managed objects for other threads.
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_));
+ }
+
+ T* pointer() { return GetOrCreateValue(); }
+ const T* pointer() const { return GetOrCreateValue(); }
+ const T& get() const { return *pointer(); }
+ void set(const T& value) { *pointer() = value; }
+
+ private:
+ // Holds a value of type T.
+ class ValueHolder : public ThreadLocalValueHolderBase {
+ public:
+ explicit ValueHolder(const T& value) : value_(value) {}
+
+ T* pointer() { return &value_; }
+
+ private:
+ T value_;
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder);
+ };
+
+ static pthread_key_t CreateKey() {
+ pthread_key_t key;
+ // When a thread exits, DeleteThreadLocalValue() will be called on
+ // the object managed for that thread.
+ GTEST_CHECK_POSIX_SUCCESS_(
+ pthread_key_create(&key, &DeleteThreadLocalValue));
+ return key;
+ }
+
+ T* GetOrCreateValue() const {
+ ThreadLocalValueHolderBase* const holder =
+ static_cast<ThreadLocalValueHolderBase*>(pthread_getspecific(key_));
+ if (holder != NULL) {
+ return CheckedDowncastToActualType<ValueHolder>(holder)->pointer();
+ }
+
+ ValueHolder* const new_holder = new ValueHolder(default_);
+ ThreadLocalValueHolderBase* const holder_base = new_holder;
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base));
+ return new_holder->pointer();
+ }
+
+ // A key pthreads uses for looking up per-thread values.
+ const pthread_key_t key_;
+ const T default_; // The default value for each thread.
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal);
+};
+
+# define GTEST_IS_THREADSAFE 1
+
+#else // GTEST_HAS_PTHREAD
+
+// A dummy implementation of synchronization primitives (mutex, lock,
+// and thread-local variable). Necessary for compiling Google Test where
+// mutex is not supported - using Google Test in multiple threads is not
+// supported on such platforms.
+
+class Mutex {
+ public:
+ Mutex() {}
+ void Lock() {}
+ void Unlock() {}
+ void AssertHeld() const {}
+};
+
+# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \
+ extern ::testing::internal::Mutex mutex
+
+# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex
+
+class GTestMutexLock {
+ public:
+ explicit GTestMutexLock(Mutex*) {} // NOLINT
+};
+
+typedef GTestMutexLock MutexLock;
+
+template <typename T>
+class ThreadLocal {
+ public:
+ ThreadLocal() : value_() {}
+ explicit ThreadLocal(const T& value) : value_(value) {}
+ T* pointer() { return &value_; }
+ const T* pointer() const { return &value_; }
+ const T& get() const { return value_; }
+ void set(const T& value) { value_ = value; }
+ private:
+ T value_;
+};
+
+// The above synchronization primitives have dummy implementations.
+// Therefore Google Test is not thread-safe.
+# define GTEST_IS_THREADSAFE 0
+
+#endif // GTEST_HAS_PTHREAD
+
+// Returns the number of threads running in the process, or 0 to indicate that
+// we cannot detect it.
+GTEST_API_ size_t GetThreadCount();
+
+// Passing non-POD classes through ellipsis (...) crashes the ARM
+// compiler and generates a warning in Sun Studio. The Nokia Symbian
+// and the IBM XL C/C++ compiler try to instantiate a copy constructor
+// for objects passed through ellipsis (...), failing for uncopyable
+// objects. We define this to ensure that only POD is passed through
+// ellipsis on these systems.
+#if defined(__SYMBIAN32__) || defined(__IBMCPP__) || defined(__SUNPRO_CC)
+// We lose support for NULL detection where the compiler doesn't like
+// passing non-POD classes through ellipsis (...).
+# define GTEST_ELLIPSIS_NEEDS_POD_ 1
+#else
+# define GTEST_CAN_COMPARE_NULL 1
+#endif
+
+// The Nokia Symbian and IBM XL C/C++ compilers cannot decide between
+// const T& and const T* in a function template. These compilers
+// _can_ decide between class template specializations for T and T*,
+// so a tr1::type_traits-like is_pointer works.
+#if defined(__SYMBIAN32__) || defined(__IBMCPP__)
+# define GTEST_NEEDS_IS_POINTER_ 1
+#endif
+
+template <bool bool_value>
+struct bool_constant {
+ typedef bool_constant<bool_value> type;
+ static const bool value = bool_value;
+};
+template <bool bool_value> const bool bool_constant<bool_value>::value;
+
+typedef bool_constant<false> false_type;
+typedef bool_constant<true> true_type;
+
+template <typename T>
+struct is_pointer : public false_type {};
+
+template <typename T>
+struct is_pointer<T*> : public true_type {};
+
+template <typename Iterator>
+struct IteratorTraits {
+ typedef typename Iterator::value_type value_type;
+};
+
+template <typename T>
+struct IteratorTraits<T*> {
+ typedef T value_type;
+};
+
+template <typename T>
+struct IteratorTraits<const T*> {
+ typedef T value_type;
+};
+
+#if GTEST_OS_WINDOWS
+# define GTEST_PATH_SEP_ "\\"
+# define GTEST_HAS_ALT_PATH_SEP_ 1
+// The biggest signed integer type the compiler supports.
+typedef __int64 BiggestInt;
+#else
+# define GTEST_PATH_SEP_ "/"
+# define GTEST_HAS_ALT_PATH_SEP_ 0
+typedef long long BiggestInt; // NOLINT
+#endif // GTEST_OS_WINDOWS
+
+// Utilities for char.
+
+// isspace(int ch) and friends accept an unsigned char or EOF. char
+// may be signed, depending on the compiler (or compiler flags).
+// Therefore we need to cast a char to unsigned char before calling
+// isspace(), etc.
+
+inline bool IsAlpha(char ch) {
+ return isalpha(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsAlNum(char ch) {
+ return isalnum(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsDigit(char ch) {
+ return isdigit(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsLower(char ch) {
+ return islower(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsSpace(char ch) {
+ return isspace(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsUpper(char ch) {
+ return isupper(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsXDigit(char ch) {
+ return isxdigit(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsXDigit(wchar_t ch) {
+ const unsigned char low_byte = static_cast<unsigned char>(ch);
+ return ch == low_byte && isxdigit(low_byte) != 0;
+}
+
+inline char ToLower(char ch) {
+ return static_cast<char>(tolower(static_cast<unsigned char>(ch)));
+}
+inline char ToUpper(char ch) {
+ return static_cast<char>(toupper(static_cast<unsigned char>(ch)));
+}
+
+// The testing::internal::posix namespace holds wrappers for common
+// POSIX functions. These wrappers hide the differences between
+// Windows/MSVC and POSIX systems. Since some compilers define these
+// standard functions as macros, the wrapper cannot have the same name
+// as the wrapped function.
+
+namespace posix {
+
+// Functions with a different name on Windows.
+
+#if GTEST_OS_WINDOWS
+
+typedef struct _stat StatStruct;
+
+# ifdef __BORLANDC__
+inline int IsATTY(int fd) { return isatty(fd); }
+inline int StrCaseCmp(const char* s1, const char* s2) {
+ return stricmp(s1, s2);
+}
+inline char* StrDup(const char* src) { return strdup(src); }
+# else // !__BORLANDC__
+# if GTEST_OS_WINDOWS_MOBILE
+inline int IsATTY(int /* fd */) { return 0; }
+# else
+inline int IsATTY(int fd) { return _isatty(fd); }
+# endif // GTEST_OS_WINDOWS_MOBILE
+inline int StrCaseCmp(const char* s1, const char* s2) {
+ return _stricmp(s1, s2);
+}
+inline char* StrDup(const char* src) { return _strdup(src); }
+# endif // __BORLANDC__
+
+# if GTEST_OS_WINDOWS_MOBILE
+inline int FileNo(FILE* file) { return reinterpret_cast<int>(_fileno(file)); }
+// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this
+// time and thus not defined there.
+# else
+inline int FileNo(FILE* file) { return _fileno(file); }
+inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); }
+inline int RmDir(const char* dir) { return _rmdir(dir); }
+inline bool IsDir(const StatStruct& st) {
+ return (_S_IFDIR & st.st_mode) != 0;
+}
+# endif // GTEST_OS_WINDOWS_MOBILE
+
+#else
+
+typedef struct stat StatStruct;
+
+inline int FileNo(FILE* file) { return fileno(file); }
+inline int IsATTY(int fd) { return isatty(fd); }
+inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); }
+inline int StrCaseCmp(const char* s1, const char* s2) {
+ return strcasecmp(s1, s2);
+}
+inline char* StrDup(const char* src) { return strdup(src); }
+inline int RmDir(const char* dir) { return rmdir(dir); }
+inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); }
+
+#endif // GTEST_OS_WINDOWS
+
+// Functions deprecated by MSVC 8.0.
+
+#ifdef _MSC_VER
+// Temporarily disable warning 4996 (deprecated function).
+# pragma warning(push)
+# pragma warning(disable:4996)
+#endif
+
+inline const char* StrNCpy(char* dest, const char* src, size_t n) {
+ return strncpy(dest, src, n);
+}
+
+// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and
+// StrError() aren't needed on Windows CE at this time and thus not
+// defined there.
+
+#if !GTEST_OS_WINDOWS_MOBILE
+inline int ChDir(const char* dir) { return chdir(dir); }
+#endif
+inline FILE* FOpen(const char* path, const char* mode) {
+ return fopen(path, mode);
+}
+#if !GTEST_OS_WINDOWS_MOBILE
+inline FILE *FReopen(const char* path, const char* mode, FILE* stream) {
+ return freopen(path, mode, stream);
+}
+inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); }
+#endif
+inline int FClose(FILE* fp) { return fclose(fp); }
+#if !GTEST_OS_WINDOWS_MOBILE
+inline int Read(int fd, void* buf, unsigned int count) {
+ return static_cast<int>(read(fd, buf, count));
+}
+inline int Write(int fd, const void* buf, unsigned int count) {
+ return static_cast<int>(write(fd, buf, count));
+}
+inline int Close(int fd) { return close(fd); }
+inline const char* StrError(int errnum) { return strerror(errnum); }
+#endif
+inline const char* GetEnv(const char* name) {
+#if GTEST_OS_WINDOWS_MOBILE
+ // We are on Windows CE, which has no environment variables.
+ return NULL;
+#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9)
+ // Environment variables which we programmatically clear will be set to the
+ // empty string rather than unset (NULL). Handle that case.
+ const char* const env = getenv(name);
+ return (env != NULL && env[0] != '\0') ? env : NULL;
+#else
+ return getenv(name);
+#endif
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop) // Restores the warning state.
+#endif
+
+#if GTEST_OS_WINDOWS_MOBILE
+// Windows CE has no C library. The abort() function is used in
+// several places in Google Test. This implementation provides a reasonable
+// imitation of standard behaviour.
+void Abort();
+#else
+inline void Abort() { abort(); }
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+} // namespace posix
+
+// MSVC "deprecates" snprintf and issues warnings wherever it is used. In
+// order to avoid these warnings, we need to use _snprintf or _snprintf_s on
+// MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate
+// function in order to achieve that. We use macro definition here because
+// snprintf is a variadic function.
+#if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE
+// MSVC 2005 and above support variadic macros.
+# define GTEST_SNPRINTF_(buffer, size, format, ...) \
+ _snprintf_s(buffer, size, size, format, __VA_ARGS__)
+#elif defined(_MSC_VER)
+// Windows CE does not define _snprintf_s and MSVC prior to 2005 doesn't
+// complain about _snprintf.
+# define GTEST_SNPRINTF_ _snprintf
+#else
+# define GTEST_SNPRINTF_ snprintf
+#endif
+
+// The maximum number a BiggestInt can represent. This definition
+// works no matter BiggestInt is represented in one's complement or
+// two's complement.
+//
+// We cannot rely on numeric_limits in STL, as __int64 and long long
+// are not part of standard C++ and numeric_limits doesn't need to be
+// defined for them.
+const BiggestInt kMaxBiggestInt =
+ ~(static_cast<BiggestInt>(1) << (8*sizeof(BiggestInt) - 1));
+
+// This template class serves as a compile-time function from size to
+// type. It maps a size in bytes to a primitive type with that
+// size. e.g.
+//
+// TypeWithSize<4>::UInt
+//
+// is typedef-ed to be unsigned int (unsigned integer made up of 4
+// bytes).
+//
+// Such functionality should belong to STL, but I cannot find it
+// there.
+//
+// Google Test uses this class in the implementation of floating-point
+// comparison.
+//
+// For now it only handles UInt (unsigned int) as that's all Google Test
+// needs. Other types can be easily added in the future if need
+// arises.
+template <size_t size>
+class TypeWithSize {
+ public:
+ // This prevents the user from using TypeWithSize<N> with incorrect
+ // values of N.
+ typedef void UInt;
+};
+
+// The specialization for size 4.
+template <>
+class TypeWithSize<4> {
+ public:
+ // unsigned int has size 4 in both gcc and MSVC.
+ //
+ // As base/basictypes.h doesn't compile on Windows, we cannot use
+ // uint32, uint64, and etc here.
+ typedef int Int;
+ typedef unsigned int UInt;
+};
+
+// The specialization for size 8.
+template <>
+class TypeWithSize<8> {
+ public:
+#if GTEST_OS_WINDOWS
+ typedef __int64 Int;
+ typedef unsigned __int64 UInt;
+#else
+ typedef long long Int; // NOLINT
+ typedef unsigned long long UInt; // NOLINT
+#endif // GTEST_OS_WINDOWS
+};
+
+// Integer types of known sizes.
+typedef TypeWithSize<4>::Int Int32;
+typedef TypeWithSize<4>::UInt UInt32;
+typedef TypeWithSize<8>::Int Int64;
+typedef TypeWithSize<8>::UInt UInt64;
+typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds.
+
+// Utilities for command line flags and environment variables.
+
+// Macro for referencing flags.
+#define GTEST_FLAG(name) FLAGS_gtest_##name
+
+// Macros for declaring flags.
+#define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name)
+#define GTEST_DECLARE_int32_(name) \
+ GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name)
+#define GTEST_DECLARE_string_(name) \
+ GTEST_API_ extern ::std::string GTEST_FLAG(name)
+
+// Macros for defining flags.
+#define GTEST_DEFINE_bool_(name, default_val, doc) \
+ GTEST_API_ bool GTEST_FLAG(name) = (default_val)
+#define GTEST_DEFINE_int32_(name, default_val, doc) \
+ GTEST_API_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val)
+#define GTEST_DEFINE_string_(name, default_val, doc) \
+ GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val)
+
+// Thread annotations
+#define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)
+#define GTEST_LOCK_EXCLUDED_(locks)
+
+// Parses 'str' for a 32-bit signed integer. If successful, writes the result
+// to *value and returns true; otherwise leaves *value unchanged and returns
+// false.
+// TODO(chandlerc): Find a better way to refactor flag and environment parsing
+// out of both gtest-port.cc and gtest.cc to avoid exporting this utility
+// function.
+bool ParseInt32(const Message& src_text, const char* str, Int32* value);
+
+// Parses a bool/Int32/string from the environment variable
+// corresponding to the given Google Test flag.
+bool BoolFromGTestEnv(const char* flag, bool default_val);
+GTEST_API_ Int32 Int32FromGTestEnv(const char* flag, Int32 default_val);
+const char* StringFromGTestEnv(const char* flag, const char* default_val);
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-string.h b/extern/gtest/include/gtest/internal/gtest-string.h
new file mode 100644
index 00000000000..97f1a7fdd2c
--- /dev/null
+++ b/extern/gtest/include/gtest/internal/gtest-string.h
@@ -0,0 +1,167 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file declares the String class and functions used internally by
+// Google Test. They are subject to change without notice. They should not used
+// by code external to Google Test.
+//
+// This header file is #included by <gtest/internal/gtest-internal.h>.
+// It should not be #included by other files.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
+
+#ifdef __BORLANDC__
+// string.h is not guaranteed to provide strcpy on C++ Builder.
+# include <mem.h>
+#endif
+
+#include <string.h>
+#include <string>
+
+#include "gtest/internal/gtest-port.h"
+
+namespace testing {
+namespace internal {
+
+// String - an abstract class holding static string utilities.
+class GTEST_API_ String {
+ public:
+ // Static utility methods
+
+ // Clones a 0-terminated C string, allocating memory using new. The
+ // caller is responsible for deleting the return value using
+ // delete[]. Returns the cloned string, or NULL if the input is
+ // NULL.
+ //
+ // This is different from strdup() in string.h, which allocates
+ // memory using malloc().
+ static const char* CloneCString(const char* c_str);
+
+#if GTEST_OS_WINDOWS_MOBILE
+ // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be
+ // able to pass strings to Win32 APIs on CE we need to convert them
+ // to 'Unicode', UTF-16.
+
+ // Creates a UTF-16 wide string from the given ANSI string, allocating
+ // memory using new. The caller is responsible for deleting the return
+ // value using delete[]. Returns the wide string, or NULL if the
+ // input is NULL.
+ //
+ // The wide string is created using the ANSI codepage (CP_ACP) to
+ // match the behaviour of the ANSI versions of Win32 calls and the
+ // C runtime.
+ static LPCWSTR AnsiToUtf16(const char* c_str);
+
+ // Creates an ANSI string from the given wide string, allocating
+ // memory using new. The caller is responsible for deleting the return
+ // value using delete[]. Returns the ANSI string, or NULL if the
+ // input is NULL.
+ //
+ // The returned string is created using the ANSI codepage (CP_ACP) to
+ // match the behaviour of the ANSI versions of Win32 calls and the
+ // C runtime.
+ static const char* Utf16ToAnsi(LPCWSTR utf16_str);
+#endif
+
+ // Compares two C strings. Returns true iff they have the same content.
+ //
+ // Unlike strcmp(), this function can handle NULL argument(s). A
+ // NULL C string is considered different to any non-NULL C string,
+ // including the empty string.
+ static bool CStringEquals(const char* lhs, const char* rhs);
+
+ // Converts a wide C string to a String using the UTF-8 encoding.
+ // NULL will be converted to "(null)". If an error occurred during
+ // the conversion, "(failed to convert from wide string)" is
+ // returned.
+ static std::string ShowWideCString(const wchar_t* wide_c_str);
+
+ // Compares two wide C strings. Returns true iff they have the same
+ // content.
+ //
+ // Unlike wcscmp(), this function can handle NULL argument(s). A
+ // NULL C string is considered different to any non-NULL C string,
+ // including the empty string.
+ static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs);
+
+ // Compares two C strings, ignoring case. Returns true iff they
+ // have the same content.
+ //
+ // Unlike strcasecmp(), this function can handle NULL argument(s).
+ // A NULL C string is considered different to any non-NULL C string,
+ // including the empty string.
+ static bool CaseInsensitiveCStringEquals(const char* lhs,
+ const char* rhs);
+
+ // Compares two wide C strings, ignoring case. Returns true iff they
+ // have the same content.
+ //
+ // Unlike wcscasecmp(), this function can handle NULL argument(s).
+ // A NULL C string is considered different to any non-NULL wide C string,
+ // including the empty string.
+ // NB: The implementations on different platforms slightly differ.
+ // On windows, this method uses _wcsicmp which compares according to LC_CTYPE
+ // environment variable. On GNU platform this method uses wcscasecmp
+ // which compares according to LC_CTYPE category of the current locale.
+ // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the
+ // current locale.
+ static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs,
+ const wchar_t* rhs);
+
+ // Returns true iff the given string ends with the given suffix, ignoring
+ // case. Any string is considered to end with an empty suffix.
+ static bool EndsWithCaseInsensitive(
+ const std::string& str, const std::string& suffix);
+
+ // Formats an int value as "%02d".
+ static std::string FormatIntWidth2(int value); // "%02d" for width == 2
+
+ // Formats an int value as "%X".
+ static std::string FormatHexInt(int value);
+
+ // Formats a byte as "%02X".
+ static std::string FormatByte(unsigned char value);
+
+ private:
+ String(); // Not meant to be instantiated.
+}; // class String
+
+// Gets the content of the stringstream's buffer as an std::string. Each '\0'
+// character in the buffer is replaced with "\\0".
+GTEST_API_ std::string StringStreamToString(::std::stringstream* stream);
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-tuple.h b/extern/gtest/include/gtest/internal/gtest-tuple.h
new file mode 100644
index 00000000000..7b3dfc312dc
--- /dev/null
+++ b/extern/gtest/include/gtest/internal/gtest-tuple.h
@@ -0,0 +1,1012 @@
+// This file was GENERATED by command:
+// pump.py gtest-tuple.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2009 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Implements a subset of TR1 tuple needed by Google Test and Google Mock.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
+
+#include <utility> // For ::std::pair.
+
+// The compiler used in Symbian has a bug that prevents us from declaring the
+// tuple template as a friend (it complains that tuple is redefined). This
+// hack bypasses the bug by declaring the members that should otherwise be
+// private as public.
+// Sun Studio versions < 12 also have the above bug.
+#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590)
+# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public:
+#else
+# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \
+ template <GTEST_10_TYPENAMES_(U)> friend class tuple; \
+ private:
+#endif
+
+// GTEST_n_TUPLE_(T) is the type of an n-tuple.
+#define GTEST_0_TUPLE_(T) tuple<>
+#define GTEST_1_TUPLE_(T) tuple<T##0, void, void, void, void, void, void, \
+ void, void, void>
+#define GTEST_2_TUPLE_(T) tuple<T##0, T##1, void, void, void, void, void, \
+ void, void, void>
+#define GTEST_3_TUPLE_(T) tuple<T##0, T##1, T##2, void, void, void, void, \
+ void, void, void>
+#define GTEST_4_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, void, void, void, \
+ void, void, void>
+#define GTEST_5_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, void, void, \
+ void, void, void>
+#define GTEST_6_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, void, \
+ void, void, void>
+#define GTEST_7_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
+ void, void, void>
+#define GTEST_8_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
+ T##7, void, void>
+#define GTEST_9_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
+ T##7, T##8, void>
+#define GTEST_10_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
+ T##7, T##8, T##9>
+
+// GTEST_n_TYPENAMES_(T) declares a list of n typenames.
+#define GTEST_0_TYPENAMES_(T)
+#define GTEST_1_TYPENAMES_(T) typename T##0
+#define GTEST_2_TYPENAMES_(T) typename T##0, typename T##1
+#define GTEST_3_TYPENAMES_(T) typename T##0, typename T##1, typename T##2
+#define GTEST_4_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3
+#define GTEST_5_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4
+#define GTEST_6_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4, typename T##5
+#define GTEST_7_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4, typename T##5, typename T##6
+#define GTEST_8_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4, typename T##5, typename T##6, typename T##7
+#define GTEST_9_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4, typename T##5, typename T##6, \
+ typename T##7, typename T##8
+#define GTEST_10_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+ typename T##3, typename T##4, typename T##5, typename T##6, \
+ typename T##7, typename T##8, typename T##9
+
+// In theory, defining stuff in the ::std namespace is undefined
+// behavior. We can do this as we are playing the role of a standard
+// library vendor.
+namespace std {
+namespace tr1 {
+
+template <typename T0 = void, typename T1 = void, typename T2 = void,
+ typename T3 = void, typename T4 = void, typename T5 = void,
+ typename T6 = void, typename T7 = void, typename T8 = void,
+ typename T9 = void>
+class tuple;
+
+// Anything in namespace gtest_internal is Google Test's INTERNAL
+// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code.
+namespace gtest_internal {
+
+// ByRef<T>::type is T if T is a reference; otherwise it's const T&.
+template <typename T>
+struct ByRef { typedef const T& type; }; // NOLINT
+template <typename T>
+struct ByRef<T&> { typedef T& type; }; // NOLINT
+
+// A handy wrapper for ByRef.
+#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef<T>::type
+
+// AddRef<T>::type is T if T is a reference; otherwise it's T&. This
+// is the same as tr1::add_reference<T>::type.
+template <typename T>
+struct AddRef { typedef T& type; }; // NOLINT
+template <typename T>
+struct AddRef<T&> { typedef T& type; }; // NOLINT
+
+// A handy wrapper for AddRef.
+#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef<T>::type
+
+// A helper for implementing get<k>().
+template <int k> class Get;
+
+// A helper for implementing tuple_element<k, T>. kIndexValid is true
+// iff k < the number of fields in tuple type T.
+template <bool kIndexValid, int kIndex, class Tuple>
+struct TupleElement;
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 0, GTEST_10_TUPLE_(T) > {
+ typedef T0 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 1, GTEST_10_TUPLE_(T) > {
+ typedef T1 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 2, GTEST_10_TUPLE_(T) > {
+ typedef T2 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 3, GTEST_10_TUPLE_(T) > {
+ typedef T3 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 4, GTEST_10_TUPLE_(T) > {
+ typedef T4 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 5, GTEST_10_TUPLE_(T) > {
+ typedef T5 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 6, GTEST_10_TUPLE_(T) > {
+ typedef T6 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 7, GTEST_10_TUPLE_(T) > {
+ typedef T7 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 8, GTEST_10_TUPLE_(T) > {
+ typedef T8 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 9, GTEST_10_TUPLE_(T) > {
+ typedef T9 type;
+};
+
+} // namespace gtest_internal
+
+template <>
+class tuple<> {
+ public:
+ tuple() {}
+ tuple(const tuple& /* t */) {}
+ tuple& operator=(const tuple& /* t */) { return *this; }
+};
+
+template <GTEST_1_TYPENAMES_(T)>
+class GTEST_1_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0) : f0_(f0) {}
+
+ tuple(const tuple& t) : f0_(t.f0_) {}
+
+ template <GTEST_1_TYPENAMES_(U)>
+ tuple(const GTEST_1_TUPLE_(U)& t) : f0_(t.f0_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_1_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_1_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_1_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_1_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ return *this;
+ }
+
+ T0 f0_;
+};
+
+template <GTEST_2_TYPENAMES_(T)>
+class GTEST_2_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1) : f0_(f0),
+ f1_(f1) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_) {}
+
+ template <GTEST_2_TYPENAMES_(U)>
+ tuple(const GTEST_2_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_) {}
+ template <typename U0, typename U1>
+ tuple(const ::std::pair<U0, U1>& p) : f0_(p.first), f1_(p.second) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_2_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_2_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+ template <typename U0, typename U1>
+ tuple& operator=(const ::std::pair<U0, U1>& p) {
+ f0_ = p.first;
+ f1_ = p.second;
+ return *this;
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_2_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_2_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+};
+
+template <GTEST_3_TYPENAMES_(T)>
+class GTEST_3_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2) : f0_(f0), f1_(f1), f2_(f2) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {}
+
+ template <GTEST_3_TYPENAMES_(U)>
+ tuple(const GTEST_3_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_3_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_3_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_3_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_3_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+};
+
+template <GTEST_4_TYPENAMES_(T)>
+class GTEST_4_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3) : f0_(f0), f1_(f1), f2_(f2),
+ f3_(f3) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_) {}
+
+ template <GTEST_4_TYPENAMES_(U)>
+ tuple(const GTEST_4_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_4_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_4_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_4_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_4_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+};
+
+template <GTEST_5_TYPENAMES_(T)>
+class GTEST_5_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3,
+ GTEST_BY_REF_(T4) f4) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_) {}
+
+ template <GTEST_5_TYPENAMES_(U)>
+ tuple(const GTEST_5_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_5_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_5_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_5_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_5_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+};
+
+template <GTEST_6_TYPENAMES_(T)>
+class GTEST_6_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+ GTEST_BY_REF_(T5) f5) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4),
+ f5_(f5) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_), f5_(t.f5_) {}
+
+ template <GTEST_6_TYPENAMES_(U)>
+ tuple(const GTEST_6_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_), f5_(t.f5_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_6_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_6_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_6_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_6_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ f5_ = t.f5_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+ T5 f5_;
+};
+
+template <GTEST_7_TYPENAMES_(T)>
+class GTEST_7_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+ GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6) : f0_(f0), f1_(f1), f2_(f2),
+ f3_(f3), f4_(f4), f5_(f5), f6_(f6) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {}
+
+ template <GTEST_7_TYPENAMES_(U)>
+ tuple(const GTEST_7_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_7_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_7_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_7_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_7_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ f5_ = t.f5_;
+ f6_ = t.f6_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+ T5 f5_;
+ T6 f6_;
+};
+
+template <GTEST_8_TYPENAMES_(T)>
+class GTEST_8_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+ GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6,
+ GTEST_BY_REF_(T7) f7) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4),
+ f5_(f5), f6_(f6), f7_(f7) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {}
+
+ template <GTEST_8_TYPENAMES_(U)>
+ tuple(const GTEST_8_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_8_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_8_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_8_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_8_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ f5_ = t.f5_;
+ f6_ = t.f6_;
+ f7_ = t.f7_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+ T5 f5_;
+ T6 f6_;
+ T7 f7_;
+};
+
+template <GTEST_9_TYPENAMES_(T)>
+class GTEST_9_TUPLE_(T) {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+ GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7,
+ GTEST_BY_REF_(T8) f8) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4),
+ f5_(f5), f6_(f6), f7_(f7), f8_(f8) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {}
+
+ template <GTEST_9_TYPENAMES_(U)>
+ tuple(const GTEST_9_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_9_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_9_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_9_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_9_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ f5_ = t.f5_;
+ f6_ = t.f6_;
+ f7_ = t.f7_;
+ f8_ = t.f8_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+ T5 f5_;
+ T6 f6_;
+ T7 f7_;
+ T8 f8_;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+class tuple {
+ public:
+ template <int k> friend class gtest_internal::Get;
+
+ tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_(),
+ f9_() {}
+
+ explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+ GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+ GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7,
+ GTEST_BY_REF_(T8) f8, GTEST_BY_REF_(T9) f9) : f0_(f0), f1_(f1), f2_(f2),
+ f3_(f3), f4_(f4), f5_(f5), f6_(f6), f7_(f7), f8_(f8), f9_(f9) {}
+
+ tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+ f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), f9_(t.f9_) {}
+
+ template <GTEST_10_TYPENAMES_(U)>
+ tuple(const GTEST_10_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+ f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_),
+ f9_(t.f9_) {}
+
+ tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+ template <GTEST_10_TYPENAMES_(U)>
+ tuple& operator=(const GTEST_10_TUPLE_(U)& t) {
+ return CopyFrom(t);
+ }
+
+ GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+ template <GTEST_10_TYPENAMES_(U)>
+ tuple& CopyFrom(const GTEST_10_TUPLE_(U)& t) {
+ f0_ = t.f0_;
+ f1_ = t.f1_;
+ f2_ = t.f2_;
+ f3_ = t.f3_;
+ f4_ = t.f4_;
+ f5_ = t.f5_;
+ f6_ = t.f6_;
+ f7_ = t.f7_;
+ f8_ = t.f8_;
+ f9_ = t.f9_;
+ return *this;
+ }
+
+ T0 f0_;
+ T1 f1_;
+ T2 f2_;
+ T3 f3_;
+ T4 f4_;
+ T5 f5_;
+ T6 f6_;
+ T7 f7_;
+ T8 f8_;
+ T9 f9_;
+};
+
+// 6.1.3.2 Tuple creation functions.
+
+// Known limitations: we don't support passing an
+// std::tr1::reference_wrapper<T> to make_tuple(). And we don't
+// implement tie().
+
+inline tuple<> make_tuple() { return tuple<>(); }
+
+template <GTEST_1_TYPENAMES_(T)>
+inline GTEST_1_TUPLE_(T) make_tuple(const T0& f0) {
+ return GTEST_1_TUPLE_(T)(f0);
+}
+
+template <GTEST_2_TYPENAMES_(T)>
+inline GTEST_2_TUPLE_(T) make_tuple(const T0& f0, const T1& f1) {
+ return GTEST_2_TUPLE_(T)(f0, f1);
+}
+
+template <GTEST_3_TYPENAMES_(T)>
+inline GTEST_3_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2) {
+ return GTEST_3_TUPLE_(T)(f0, f1, f2);
+}
+
+template <GTEST_4_TYPENAMES_(T)>
+inline GTEST_4_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3) {
+ return GTEST_4_TUPLE_(T)(f0, f1, f2, f3);
+}
+
+template <GTEST_5_TYPENAMES_(T)>
+inline GTEST_5_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4) {
+ return GTEST_5_TUPLE_(T)(f0, f1, f2, f3, f4);
+}
+
+template <GTEST_6_TYPENAMES_(T)>
+inline GTEST_6_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4, const T5& f5) {
+ return GTEST_6_TUPLE_(T)(f0, f1, f2, f3, f4, f5);
+}
+
+template <GTEST_7_TYPENAMES_(T)>
+inline GTEST_7_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4, const T5& f5, const T6& f6) {
+ return GTEST_7_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6);
+}
+
+template <GTEST_8_TYPENAMES_(T)>
+inline GTEST_8_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7) {
+ return GTEST_8_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7);
+}
+
+template <GTEST_9_TYPENAMES_(T)>
+inline GTEST_9_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7,
+ const T8& f8) {
+ return GTEST_9_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8);
+}
+
+template <GTEST_10_TYPENAMES_(T)>
+inline GTEST_10_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+ const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7,
+ const T8& f8, const T9& f9) {
+ return GTEST_10_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9);
+}
+
+// 6.1.3.3 Tuple helper classes.
+
+template <typename Tuple> struct tuple_size;
+
+template <GTEST_0_TYPENAMES_(T)>
+struct tuple_size<GTEST_0_TUPLE_(T) > {
+ static const int value = 0;
+};
+
+template <GTEST_1_TYPENAMES_(T)>
+struct tuple_size<GTEST_1_TUPLE_(T) > {
+ static const int value = 1;
+};
+
+template <GTEST_2_TYPENAMES_(T)>
+struct tuple_size<GTEST_2_TUPLE_(T) > {
+ static const int value = 2;
+};
+
+template <GTEST_3_TYPENAMES_(T)>
+struct tuple_size<GTEST_3_TUPLE_(T) > {
+ static const int value = 3;
+};
+
+template <GTEST_4_TYPENAMES_(T)>
+struct tuple_size<GTEST_4_TUPLE_(T) > {
+ static const int value = 4;
+};
+
+template <GTEST_5_TYPENAMES_(T)>
+struct tuple_size<GTEST_5_TUPLE_(T) > {
+ static const int value = 5;
+};
+
+template <GTEST_6_TYPENAMES_(T)>
+struct tuple_size<GTEST_6_TUPLE_(T) > {
+ static const int value = 6;
+};
+
+template <GTEST_7_TYPENAMES_(T)>
+struct tuple_size<GTEST_7_TUPLE_(T) > {
+ static const int value = 7;
+};
+
+template <GTEST_8_TYPENAMES_(T)>
+struct tuple_size<GTEST_8_TUPLE_(T) > {
+ static const int value = 8;
+};
+
+template <GTEST_9_TYPENAMES_(T)>
+struct tuple_size<GTEST_9_TUPLE_(T) > {
+ static const int value = 9;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct tuple_size<GTEST_10_TUPLE_(T) > {
+ static const int value = 10;
+};
+
+template <int k, class Tuple>
+struct tuple_element {
+ typedef typename gtest_internal::TupleElement<
+ k < (tuple_size<Tuple>::value), k, Tuple>::type type;
+};
+
+#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element<k, Tuple >::type
+
+// 6.1.3.4 Element access.
+
+namespace gtest_internal {
+
+template <>
+class Get<0> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple))
+ Field(Tuple& t) { return t.f0_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple))
+ ConstField(const Tuple& t) { return t.f0_; }
+};
+
+template <>
+class Get<1> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple))
+ Field(Tuple& t) { return t.f1_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple))
+ ConstField(const Tuple& t) { return t.f1_; }
+};
+
+template <>
+class Get<2> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple))
+ Field(Tuple& t) { return t.f2_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple))
+ ConstField(const Tuple& t) { return t.f2_; }
+};
+
+template <>
+class Get<3> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple))
+ Field(Tuple& t) { return t.f3_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple))
+ ConstField(const Tuple& t) { return t.f3_; }
+};
+
+template <>
+class Get<4> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple))
+ Field(Tuple& t) { return t.f4_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple))
+ ConstField(const Tuple& t) { return t.f4_; }
+};
+
+template <>
+class Get<5> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple))
+ Field(Tuple& t) { return t.f5_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple))
+ ConstField(const Tuple& t) { return t.f5_; }
+};
+
+template <>
+class Get<6> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple))
+ Field(Tuple& t) { return t.f6_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple))
+ ConstField(const Tuple& t) { return t.f6_; }
+};
+
+template <>
+class Get<7> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple))
+ Field(Tuple& t) { return t.f7_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple))
+ ConstField(const Tuple& t) { return t.f7_; }
+};
+
+template <>
+class Get<8> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple))
+ Field(Tuple& t) { return t.f8_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple))
+ ConstField(const Tuple& t) { return t.f8_; }
+};
+
+template <>
+class Get<9> {
+ public:
+ template <class Tuple>
+ static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple))
+ Field(Tuple& t) { return t.f9_; } // NOLINT
+
+ template <class Tuple>
+ static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple))
+ ConstField(const Tuple& t) { return t.f9_; }
+};
+
+} // namespace gtest_internal
+
+template <int k, GTEST_10_TYPENAMES_(T)>
+GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T)))
+get(GTEST_10_TUPLE_(T)& t) {
+ return gtest_internal::Get<k>::Field(t);
+}
+
+template <int k, GTEST_10_TYPENAMES_(T)>
+GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T)))
+get(const GTEST_10_TUPLE_(T)& t) {
+ return gtest_internal::Get<k>::ConstField(t);
+}
+
+// 6.1.3.5 Relational operators
+
+// We only implement == and !=, as we don't have a need for the rest yet.
+
+namespace gtest_internal {
+
+// SameSizeTuplePrefixComparator<k, k>::Eq(t1, t2) returns true if the
+// first k fields of t1 equals the first k fields of t2.
+// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if
+// k1 != k2.
+template <int kSize1, int kSize2>
+struct SameSizeTuplePrefixComparator;
+
+template <>
+struct SameSizeTuplePrefixComparator<0, 0> {
+ template <class Tuple1, class Tuple2>
+ static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) {
+ return true;
+ }
+};
+
+template <int k>
+struct SameSizeTuplePrefixComparator<k, k> {
+ template <class Tuple1, class Tuple2>
+ static bool Eq(const Tuple1& t1, const Tuple2& t2) {
+ return SameSizeTuplePrefixComparator<k - 1, k - 1>::Eq(t1, t2) &&
+ ::std::tr1::get<k - 1>(t1) == ::std::tr1::get<k - 1>(t2);
+ }
+};
+
+} // namespace gtest_internal
+
+template <GTEST_10_TYPENAMES_(T), GTEST_10_TYPENAMES_(U)>
+inline bool operator==(const GTEST_10_TUPLE_(T)& t,
+ const GTEST_10_TUPLE_(U)& u) {
+ return gtest_internal::SameSizeTuplePrefixComparator<
+ tuple_size<GTEST_10_TUPLE_(T) >::value,
+ tuple_size<GTEST_10_TUPLE_(U) >::value>::Eq(t, u);
+}
+
+template <GTEST_10_TYPENAMES_(T), GTEST_10_TYPENAMES_(U)>
+inline bool operator!=(const GTEST_10_TUPLE_(T)& t,
+ const GTEST_10_TUPLE_(U)& u) { return !(t == u); }
+
+// 6.1.4 Pairs.
+// Unimplemented.
+
+} // namespace tr1
+} // namespace std
+
+#undef GTEST_0_TUPLE_
+#undef GTEST_1_TUPLE_
+#undef GTEST_2_TUPLE_
+#undef GTEST_3_TUPLE_
+#undef GTEST_4_TUPLE_
+#undef GTEST_5_TUPLE_
+#undef GTEST_6_TUPLE_
+#undef GTEST_7_TUPLE_
+#undef GTEST_8_TUPLE_
+#undef GTEST_9_TUPLE_
+#undef GTEST_10_TUPLE_
+
+#undef GTEST_0_TYPENAMES_
+#undef GTEST_1_TYPENAMES_
+#undef GTEST_2_TYPENAMES_
+#undef GTEST_3_TYPENAMES_
+#undef GTEST_4_TYPENAMES_
+#undef GTEST_5_TYPENAMES_
+#undef GTEST_6_TYPENAMES_
+#undef GTEST_7_TYPENAMES_
+#undef GTEST_8_TYPENAMES_
+#undef GTEST_9_TYPENAMES_
+#undef GTEST_10_TYPENAMES_
+
+#undef GTEST_DECLARE_TUPLE_AS_FRIEND_
+#undef GTEST_BY_REF_
+#undef GTEST_ADD_REF_
+#undef GTEST_TUPLE_ELEMENT_
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-type-util.h b/extern/gtest/include/gtest/internal/gtest-type-util.h
new file mode 100644
index 00000000000..e46f7cfcb48
--- /dev/null
+++ b/extern/gtest/include/gtest/internal/gtest-type-util.h
@@ -0,0 +1,3331 @@
+// This file was GENERATED by command:
+// pump.py gtest-type-util.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Type utilities needed for implementing typed and type-parameterized
+// tests. This file is generated by a SCRIPT. DO NOT EDIT BY HAND!
+//
+// Currently we support at most 50 types in a list, and at most 50
+// type-parameterized tests in one type-parameterized test case.
+// Please contact googletestframework@googlegroups.com if you need
+// more.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
+
+#include "gtest/internal/gtest-port.h"
+
+// #ifdef __GNUC__ is too general here. It is possible to use gcc without using
+// libstdc++ (which is where cxxabi.h comes from).
+# if GTEST_HAS_CXXABI_H_
+# include <cxxabi.h>
+# elif defined(__HP_aCC)
+# include <acxx_demangle.h>
+# endif // GTEST_HASH_CXXABI_H_
+
+namespace testing {
+namespace internal {
+
+// GetTypeName<T>() returns a human-readable name of type T.
+// NB: This function is also used in Google Mock, so don't move it inside of
+// the typed-test-only section below.
+template <typename T>
+std::string GetTypeName() {
+# if GTEST_HAS_RTTI
+
+ const char* const name = typeid(T).name();
+# if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC)
+ int status = 0;
+ // gcc's implementation of typeid(T).name() mangles the type name,
+ // so we have to demangle it.
+# if GTEST_HAS_CXXABI_H_
+ using abi::__cxa_demangle;
+# endif // GTEST_HAS_CXXABI_H_
+ char* const readable_name = __cxa_demangle(name, 0, 0, &status);
+ const std::string name_str(status == 0 ? readable_name : name);
+ free(readable_name);
+ return name_str;
+# else
+ return name;
+# endif // GTEST_HAS_CXXABI_H_ || __HP_aCC
+
+# else
+
+ return "<type>";
+
+# endif // GTEST_HAS_RTTI
+}
+
+#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
+
+// AssertyTypeEq<T1, T2>::type is defined iff T1 and T2 are the same
+// type. This can be used as a compile-time assertion to ensure that
+// two types are equal.
+
+template <typename T1, typename T2>
+struct AssertTypeEq;
+
+template <typename T>
+struct AssertTypeEq<T, T> {
+ typedef bool type;
+};
+
+// A unique type used as the default value for the arguments of class
+// template Types. This allows us to simulate variadic templates
+// (e.g. Types<int>, Type<int, double>, and etc), which C++ doesn't
+// support directly.
+struct None {};
+
+// The following family of struct and struct templates are used to
+// represent type lists. In particular, TypesN<T1, T2, ..., TN>
+// represents a type list with N types (T1, T2, ..., and TN) in it.
+// Except for Types0, every struct in the family has two member types:
+// Head for the first type in the list, and Tail for the rest of the
+// list.
+
+// The empty type list.
+struct Types0 {};
+
+// Type lists of length 1, 2, 3, and so on.
+
+template <typename T1>
+struct Types1 {
+ typedef T1 Head;
+ typedef Types0 Tail;
+};
+template <typename T1, typename T2>
+struct Types2 {
+ typedef T1 Head;
+ typedef Types1<T2> Tail;
+};
+
+template <typename T1, typename T2, typename T3>
+struct Types3 {
+ typedef T1 Head;
+ typedef Types2<T2, T3> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4>
+struct Types4 {
+ typedef T1 Head;
+ typedef Types3<T2, T3, T4> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+struct Types5 {
+ typedef T1 Head;
+ typedef Types4<T2, T3, T4, T5> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+struct Types6 {
+ typedef T1 Head;
+ typedef Types5<T2, T3, T4, T5, T6> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+struct Types7 {
+ typedef T1 Head;
+ typedef Types6<T2, T3, T4, T5, T6, T7> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+struct Types8 {
+ typedef T1 Head;
+ typedef Types7<T2, T3, T4, T5, T6, T7, T8> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+struct Types9 {
+ typedef T1 Head;
+ typedef Types8<T2, T3, T4, T5, T6, T7, T8, T9> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+struct Types10 {
+ typedef T1 Head;
+ typedef Types9<T2, T3, T4, T5, T6, T7, T8, T9, T10> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11>
+struct Types11 {
+ typedef T1 Head;
+ typedef Types10<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12>
+struct Types12 {
+ typedef T1 Head;
+ typedef Types11<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13>
+struct Types13 {
+ typedef T1 Head;
+ typedef Types12<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14>
+struct Types14 {
+ typedef T1 Head;
+ typedef Types13<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15>
+struct Types15 {
+ typedef T1 Head;
+ typedef Types14<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16>
+struct Types16 {
+ typedef T1 Head;
+ typedef Types15<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17>
+struct Types17 {
+ typedef T1 Head;
+ typedef Types16<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18>
+struct Types18 {
+ typedef T1 Head;
+ typedef Types17<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19>
+struct Types19 {
+ typedef T1 Head;
+ typedef Types18<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20>
+struct Types20 {
+ typedef T1 Head;
+ typedef Types19<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21>
+struct Types21 {
+ typedef T1 Head;
+ typedef Types20<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22>
+struct Types22 {
+ typedef T1 Head;
+ typedef Types21<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23>
+struct Types23 {
+ typedef T1 Head;
+ typedef Types22<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24>
+struct Types24 {
+ typedef T1 Head;
+ typedef Types23<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25>
+struct Types25 {
+ typedef T1 Head;
+ typedef Types24<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26>
+struct Types26 {
+ typedef T1 Head;
+ typedef Types25<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27>
+struct Types27 {
+ typedef T1 Head;
+ typedef Types26<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28>
+struct Types28 {
+ typedef T1 Head;
+ typedef Types27<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29>
+struct Types29 {
+ typedef T1 Head;
+ typedef Types28<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30>
+struct Types30 {
+ typedef T1 Head;
+ typedef Types29<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31>
+struct Types31 {
+ typedef T1 Head;
+ typedef Types30<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32>
+struct Types32 {
+ typedef T1 Head;
+ typedef Types31<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33>
+struct Types33 {
+ typedef T1 Head;
+ typedef Types32<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34>
+struct Types34 {
+ typedef T1 Head;
+ typedef Types33<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35>
+struct Types35 {
+ typedef T1 Head;
+ typedef Types34<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36>
+struct Types36 {
+ typedef T1 Head;
+ typedef Types35<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37>
+struct Types37 {
+ typedef T1 Head;
+ typedef Types36<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38>
+struct Types38 {
+ typedef T1 Head;
+ typedef Types37<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39>
+struct Types39 {
+ typedef T1 Head;
+ typedef Types38<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40>
+struct Types40 {
+ typedef T1 Head;
+ typedef Types39<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41>
+struct Types41 {
+ typedef T1 Head;
+ typedef Types40<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42>
+struct Types42 {
+ typedef T1 Head;
+ typedef Types41<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43>
+struct Types43 {
+ typedef T1 Head;
+ typedef Types42<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44>
+struct Types44 {
+ typedef T1 Head;
+ typedef Types43<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45>
+struct Types45 {
+ typedef T1 Head;
+ typedef Types44<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46>
+struct Types46 {
+ typedef T1 Head;
+ typedef Types45<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47>
+struct Types47 {
+ typedef T1 Head;
+ typedef Types46<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48>
+struct Types48 {
+ typedef T1 Head;
+ typedef Types47<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47, T48> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48, typename T49>
+struct Types49 {
+ typedef T1 Head;
+ typedef Types48<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47, T48, T49> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48, typename T49, typename T50>
+struct Types50 {
+ typedef T1 Head;
+ typedef Types49<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47, T48, T49, T50> Tail;
+};
+
+
+} // namespace internal
+
+// We don't want to require the users to write TypesN<...> directly,
+// as that would require them to count the length. Types<...> is much
+// easier to write, but generates horrible messages when there is a
+// compiler error, as gcc insists on printing out each template
+// argument, even if it has the default value (this means Types<int>
+// will appear as Types<int, None, None, ..., None> in the compiler
+// errors).
+//
+// Our solution is to combine the best part of the two approaches: a
+// user would write Types<T1, ..., TN>, and Google Test will translate
+// that to TypesN<T1, ..., TN> internally to make error messages
+// readable. The translation is done by the 'type' member of the
+// Types template.
+template <typename T1 = internal::None, typename T2 = internal::None,
+ typename T3 = internal::None, typename T4 = internal::None,
+ typename T5 = internal::None, typename T6 = internal::None,
+ typename T7 = internal::None, typename T8 = internal::None,
+ typename T9 = internal::None, typename T10 = internal::None,
+ typename T11 = internal::None, typename T12 = internal::None,
+ typename T13 = internal::None, typename T14 = internal::None,
+ typename T15 = internal::None, typename T16 = internal::None,
+ typename T17 = internal::None, typename T18 = internal::None,
+ typename T19 = internal::None, typename T20 = internal::None,
+ typename T21 = internal::None, typename T22 = internal::None,
+ typename T23 = internal::None, typename T24 = internal::None,
+ typename T25 = internal::None, typename T26 = internal::None,
+ typename T27 = internal::None, typename T28 = internal::None,
+ typename T29 = internal::None, typename T30 = internal::None,
+ typename T31 = internal::None, typename T32 = internal::None,
+ typename T33 = internal::None, typename T34 = internal::None,
+ typename T35 = internal::None, typename T36 = internal::None,
+ typename T37 = internal::None, typename T38 = internal::None,
+ typename T39 = internal::None, typename T40 = internal::None,
+ typename T41 = internal::None, typename T42 = internal::None,
+ typename T43 = internal::None, typename T44 = internal::None,
+ typename T45 = internal::None, typename T46 = internal::None,
+ typename T47 = internal::None, typename T48 = internal::None,
+ typename T49 = internal::None, typename T50 = internal::None>
+struct Types {
+ typedef internal::Types50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43, T44, T45, T46, T47, T48, T49, T50> type;
+};
+
+template <>
+struct Types<internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types0 type;
+};
+template <typename T1>
+struct Types<T1, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types1<T1> type;
+};
+template <typename T1, typename T2>
+struct Types<T1, T2, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types2<T1, T2> type;
+};
+template <typename T1, typename T2, typename T3>
+struct Types<T1, T2, T3, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types3<T1, T2, T3> type;
+};
+template <typename T1, typename T2, typename T3, typename T4>
+struct Types<T1, T2, T3, T4, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types4<T1, T2, T3, T4> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+struct Types<T1, T2, T3, T4, T5, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types5<T1, T2, T3, T4, T5> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+struct Types<T1, T2, T3, T4, T5, T6, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types6<T1, T2, T3, T4, T5, T6> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+struct Types<T1, T2, T3, T4, T5, T6, T7, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types7<T1, T2, T3, T4, T5, T6, T7> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types8<T1, T2, T3, T4, T5, T6, T7, T8> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types9<T1, T2, T3, T4, T5, T6, T7, T8, T9> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+ T12> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+ T26> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+ T40> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, internal::None,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None, internal::None> {
+ typedef internal::Types43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None, internal::None> {
+ typedef internal::Types44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43, T44> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
+ internal::None, internal::None, internal::None, internal::None,
+ internal::None> {
+ typedef internal::Types45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43, T44, T45> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
+ T46, internal::None, internal::None, internal::None, internal::None> {
+ typedef internal::Types46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43, T44, T45, T46> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
+ T46, T47, internal::None, internal::None, internal::None> {
+ typedef internal::Types47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43, T44, T45, T46, T47> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
+ T46, T47, T48, internal::None, internal::None> {
+ typedef internal::Types48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43, T44, T45, T46, T47, T48> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48, typename T49>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+ T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+ T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
+ T46, T47, T48, T49, internal::None> {
+ typedef internal::Types49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43, T44, T45, T46, T47, T48, T49> type;
+};
+
+namespace internal {
+
+# define GTEST_TEMPLATE_ template <typename T> class
+
+// The template "selector" struct TemplateSel<Tmpl> is used to
+// represent Tmpl, which must be a class template with one type
+// parameter, as a type. TemplateSel<Tmpl>::Bind<T>::type is defined
+// as the type Tmpl<T>. This allows us to actually instantiate the
+// template "selected" by TemplateSel<Tmpl>.
+//
+// This trick is necessary for simulating typedef for class templates,
+// which C++ doesn't support directly.
+template <GTEST_TEMPLATE_ Tmpl>
+struct TemplateSel {
+ template <typename T>
+ struct Bind {
+ typedef Tmpl<T> type;
+ };
+};
+
+# define GTEST_BIND_(TmplSel, T) \
+ TmplSel::template Bind<T>::type
+
+// A unique struct template used as the default value for the
+// arguments of class template Templates. This allows us to simulate
+// variadic templates (e.g. Templates<int>, Templates<int, double>,
+// and etc), which C++ doesn't support directly.
+template <typename T>
+struct NoneT {};
+
+// The following family of struct and struct templates are used to
+// represent template lists. In particular, TemplatesN<T1, T2, ...,
+// TN> represents a list of N templates (T1, T2, ..., and TN). Except
+// for Templates0, every struct in the family has two member types:
+// Head for the selector of the first template in the list, and Tail
+// for the rest of the list.
+
+// The empty template list.
+struct Templates0 {};
+
+// Template lists of length 1, 2, 3, and so on.
+
+template <GTEST_TEMPLATE_ T1>
+struct Templates1 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates0 Tail;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2>
+struct Templates2 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates1<T2> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3>
+struct Templates3 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates2<T2, T3> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4>
+struct Templates4 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates3<T2, T3, T4> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5>
+struct Templates5 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates4<T2, T3, T4, T5> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6>
+struct Templates6 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates5<T2, T3, T4, T5, T6> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7>
+struct Templates7 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates6<T2, T3, T4, T5, T6, T7> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8>
+struct Templates8 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates7<T2, T3, T4, T5, T6, T7, T8> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9>
+struct Templates9 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates8<T2, T3, T4, T5, T6, T7, T8, T9> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10>
+struct Templates10 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates9<T2, T3, T4, T5, T6, T7, T8, T9, T10> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11>
+struct Templates11 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates10<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12>
+struct Templates12 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates11<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13>
+struct Templates13 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates12<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14>
+struct Templates14 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates13<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15>
+struct Templates15 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates14<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16>
+struct Templates16 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates15<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17>
+struct Templates17 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates16<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18>
+struct Templates18 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates17<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19>
+struct Templates19 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates18<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20>
+struct Templates20 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates19<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21>
+struct Templates21 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates20<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22>
+struct Templates22 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates21<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23>
+struct Templates23 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates22<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24>
+struct Templates24 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates23<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25>
+struct Templates25 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates24<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26>
+struct Templates26 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates25<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27>
+struct Templates27 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates26<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28>
+struct Templates28 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates27<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29>
+struct Templates29 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates28<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30>
+struct Templates30 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates29<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31>
+struct Templates31 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates30<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32>
+struct Templates32 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates31<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33>
+struct Templates33 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates32<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34>
+struct Templates34 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates33<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35>
+struct Templates35 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates34<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36>
+struct Templates36 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates35<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37>
+struct Templates37 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates36<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38>
+struct Templates38 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates37<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39>
+struct Templates39 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates38<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40>
+struct Templates40 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates39<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41>
+struct Templates41 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates40<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42>
+struct Templates42 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates41<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43>
+struct Templates43 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates42<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44>
+struct Templates44 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates43<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43, T44> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45>
+struct Templates45 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates44<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43, T44, T45> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46>
+struct Templates46 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates45<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43, T44, T45, T46> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47>
+struct Templates47 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates46<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43, T44, T45, T46, T47> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48>
+struct Templates48 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates47<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43, T44, T45, T46, T47, T48> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48,
+ GTEST_TEMPLATE_ T49>
+struct Templates49 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates48<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43, T44, T45, T46, T47, T48, T49> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48,
+ GTEST_TEMPLATE_ T49, GTEST_TEMPLATE_ T50>
+struct Templates50 {
+ typedef TemplateSel<T1> Head;
+ typedef Templates49<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+ T43, T44, T45, T46, T47, T48, T49, T50> Tail;
+};
+
+
+// We don't want to require the users to write TemplatesN<...> directly,
+// as that would require them to count the length. Templates<...> is much
+// easier to write, but generates horrible messages when there is a
+// compiler error, as gcc insists on printing out each template
+// argument, even if it has the default value (this means Templates<list>
+// will appear as Templates<list, NoneT, NoneT, ..., NoneT> in the compiler
+// errors).
+//
+// Our solution is to combine the best part of the two approaches: a
+// user would write Templates<T1, ..., TN>, and Google Test will translate
+// that to TemplatesN<T1, ..., TN> internally to make error messages
+// readable. The translation is done by the 'type' member of the
+// Templates template.
+template <GTEST_TEMPLATE_ T1 = NoneT, GTEST_TEMPLATE_ T2 = NoneT,
+ GTEST_TEMPLATE_ T3 = NoneT, GTEST_TEMPLATE_ T4 = NoneT,
+ GTEST_TEMPLATE_ T5 = NoneT, GTEST_TEMPLATE_ T6 = NoneT,
+ GTEST_TEMPLATE_ T7 = NoneT, GTEST_TEMPLATE_ T8 = NoneT,
+ GTEST_TEMPLATE_ T9 = NoneT, GTEST_TEMPLATE_ T10 = NoneT,
+ GTEST_TEMPLATE_ T11 = NoneT, GTEST_TEMPLATE_ T12 = NoneT,
+ GTEST_TEMPLATE_ T13 = NoneT, GTEST_TEMPLATE_ T14 = NoneT,
+ GTEST_TEMPLATE_ T15 = NoneT, GTEST_TEMPLATE_ T16 = NoneT,
+ GTEST_TEMPLATE_ T17 = NoneT, GTEST_TEMPLATE_ T18 = NoneT,
+ GTEST_TEMPLATE_ T19 = NoneT, GTEST_TEMPLATE_ T20 = NoneT,
+ GTEST_TEMPLATE_ T21 = NoneT, GTEST_TEMPLATE_ T22 = NoneT,
+ GTEST_TEMPLATE_ T23 = NoneT, GTEST_TEMPLATE_ T24 = NoneT,
+ GTEST_TEMPLATE_ T25 = NoneT, GTEST_TEMPLATE_ T26 = NoneT,
+ GTEST_TEMPLATE_ T27 = NoneT, GTEST_TEMPLATE_ T28 = NoneT,
+ GTEST_TEMPLATE_ T29 = NoneT, GTEST_TEMPLATE_ T30 = NoneT,
+ GTEST_TEMPLATE_ T31 = NoneT, GTEST_TEMPLATE_ T32 = NoneT,
+ GTEST_TEMPLATE_ T33 = NoneT, GTEST_TEMPLATE_ T34 = NoneT,
+ GTEST_TEMPLATE_ T35 = NoneT, GTEST_TEMPLATE_ T36 = NoneT,
+ GTEST_TEMPLATE_ T37 = NoneT, GTEST_TEMPLATE_ T38 = NoneT,
+ GTEST_TEMPLATE_ T39 = NoneT, GTEST_TEMPLATE_ T40 = NoneT,
+ GTEST_TEMPLATE_ T41 = NoneT, GTEST_TEMPLATE_ T42 = NoneT,
+ GTEST_TEMPLATE_ T43 = NoneT, GTEST_TEMPLATE_ T44 = NoneT,
+ GTEST_TEMPLATE_ T45 = NoneT, GTEST_TEMPLATE_ T46 = NoneT,
+ GTEST_TEMPLATE_ T47 = NoneT, GTEST_TEMPLATE_ T48 = NoneT,
+ GTEST_TEMPLATE_ T49 = NoneT, GTEST_TEMPLATE_ T50 = NoneT>
+struct Templates {
+ typedef Templates50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42, T43, T44, T45, T46, T47, T48, T49, T50> type;
+};
+
+template <>
+struct Templates<NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT> {
+ typedef Templates0 type;
+};
+template <GTEST_TEMPLATE_ T1>
+struct Templates<T1, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT> {
+ typedef Templates1<T1> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2>
+struct Templates<T1, T2, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT> {
+ typedef Templates2<T1, T2> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3>
+struct Templates<T1, T2, T3, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates3<T1, T2, T3> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4>
+struct Templates<T1, T2, T3, T4, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates4<T1, T2, T3, T4> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5>
+struct Templates<T1, T2, T3, T4, T5, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates5<T1, T2, T3, T4, T5> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6>
+struct Templates<T1, T2, T3, T4, T5, T6, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates6<T1, T2, T3, T4, T5, T6> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates7<T1, T2, T3, T4, T5, T6, T7> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates8<T1, T2, T3, T4, T5, T6, T7, T8> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates9<T1, T2, T3, T4, T5, T6, T7, T8, T9> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT> {
+ typedef Templates22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT> {
+ typedef Templates23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT> {
+ typedef Templates24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT> {
+ typedef Templates25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT> {
+ typedef Templates26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT> {
+ typedef Templates27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT> {
+ typedef Templates28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT> {
+ typedef Templates29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, NoneT, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, NoneT, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, NoneT, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, NoneT, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, NoneT,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42, T43> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+ NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42, T43, T44> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+ T45, NoneT, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42, T43, T44, T45> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+ T45, T46, NoneT, NoneT, NoneT, NoneT> {
+ typedef Templates46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42, T43, T44, T45, T46> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+ T45, T46, T47, NoneT, NoneT, NoneT> {
+ typedef Templates47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42, T43, T44, T45, T46, T47> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+ T45, T46, T47, T48, NoneT, NoneT> {
+ typedef Templates48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42, T43, T44, T45, T46, T47, T48> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+ GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+ GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+ GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+ GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+ GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+ GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+ GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+ GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+ GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+ GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+ GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+ GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+ GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+ GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+ GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48,
+ GTEST_TEMPLATE_ T49>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+ T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+ T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+ T45, T46, T47, T48, T49, NoneT> {
+ typedef Templates49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+ T42, T43, T44, T45, T46, T47, T48, T49> type;
+};
+
+// The TypeList template makes it possible to use either a single type
+// or a Types<...> list in TYPED_TEST_CASE() and
+// INSTANTIATE_TYPED_TEST_CASE_P().
+
+template <typename T>
+struct TypeList {
+ typedef Types1<T> type;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14, typename T15,
+ typename T16, typename T17, typename T18, typename T19, typename T20,
+ typename T21, typename T22, typename T23, typename T24, typename T25,
+ typename T26, typename T27, typename T28, typename T29, typename T30,
+ typename T31, typename T32, typename T33, typename T34, typename T35,
+ typename T36, typename T37, typename T38, typename T39, typename T40,
+ typename T41, typename T42, typename T43, typename T44, typename T45,
+ typename T46, typename T47, typename T48, typename T49, typename T50>
+struct TypeList<Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+ T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+ T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+ T44, T45, T46, T47, T48, T49, T50> > {
+ typedef typename Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+ T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+ T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+ T41, T42, T43, T44, T45, T46, T47, T48, T49, T50>::type type;
+};
+
+#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
diff --git a/extern/gtest/src/gtest-all.cc b/extern/gtest/src/gtest-all.cc
new file mode 100644
index 00000000000..0a9cee52233
--- /dev/null
+++ b/extern/gtest/src/gtest-all.cc
@@ -0,0 +1,48 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: mheule@google.com (Markus Heule)
+//
+// Google C++ Testing Framework (Google Test)
+//
+// Sometimes it's desirable to build Google Test by compiling a single file.
+// This file serves this purpose.
+
+// This line ensures that gtest.h can be compiled on its own, even
+// when it's fused.
+#include "gtest/gtest.h"
+
+// The following lines pull in the real gtest *.cc files.
+#include "src/gtest.cc"
+#include "src/gtest-death-test.cc"
+#include "src/gtest-filepath.cc"
+#include "src/gtest-port.cc"
+#include "src/gtest-printers.cc"
+#include "src/gtest-test-part.cc"
+#include "src/gtest-typed-test.cc"
diff --git a/extern/gtest/src/gtest-death-test.cc b/extern/gtest/src/gtest-death-test.cc
new file mode 100644
index 00000000000..a6023fce4fa
--- /dev/null
+++ b/extern/gtest/src/gtest-death-test.cc
@@ -0,0 +1,1344 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan), vladl@google.com (Vlad Losev)
+//
+// This file implements death tests.
+
+#include "gtest/gtest-death-test.h"
+#include "gtest/internal/gtest-port.h"
+
+#if GTEST_HAS_DEATH_TEST
+
+# if GTEST_OS_MAC
+# include <crt_externs.h>
+# endif // GTEST_OS_MAC
+
+# include <errno.h>
+# include <fcntl.h>
+# include <limits.h>
+
+# if GTEST_OS_LINUX
+# include <signal.h>
+# endif // GTEST_OS_LINUX
+
+# include <stdarg.h>
+
+# if GTEST_OS_WINDOWS
+# include <windows.h>
+# else
+# include <sys/mman.h>
+# include <sys/wait.h>
+# endif // GTEST_OS_WINDOWS
+
+# if GTEST_OS_QNX
+# include <spawn.h>
+# endif // GTEST_OS_QNX
+
+#endif // GTEST_HAS_DEATH_TEST
+
+#include "gtest/gtest-message.h"
+#include "gtest/internal/gtest-string.h"
+
+// Indicates that this translation unit is part of Google Test's
+// implementation. It must come before gtest-internal-inl.h is
+// included, or there will be a compiler error. This trick is to
+// prevent a user from accidentally including gtest-internal-inl.h in
+// his code.
+#define GTEST_IMPLEMENTATION_ 1
+#include "src/gtest-internal-inl.h"
+#undef GTEST_IMPLEMENTATION_
+
+namespace testing {
+
+// Constants.
+
+// The default death test style.
+static const char kDefaultDeathTestStyle[] = "fast";
+
+GTEST_DEFINE_string_(
+ death_test_style,
+ internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle),
+ "Indicates how to run a death test in a forked child process: "
+ "\"threadsafe\" (child process re-executes the test binary "
+ "from the beginning, running only the specific death test) or "
+ "\"fast\" (child process runs the death test immediately "
+ "after forking).");
+
+GTEST_DEFINE_bool_(
+ death_test_use_fork,
+ internal::BoolFromGTestEnv("death_test_use_fork", false),
+ "Instructs to use fork()/_exit() instead of clone() in death tests. "
+ "Ignored and always uses fork() on POSIX systems where clone() is not "
+ "implemented. Useful when running under valgrind or similar tools if "
+ "those do not support clone(). Valgrind 3.3.1 will just fail if "
+ "it sees an unsupported combination of clone() flags. "
+ "It is not recommended to use this flag w/o valgrind though it will "
+ "work in 99% of the cases. Once valgrind is fixed, this flag will "
+ "most likely be removed.");
+
+namespace internal {
+GTEST_DEFINE_string_(
+ internal_run_death_test, "",
+ "Indicates the file, line number, temporal index of "
+ "the single death test to run, and a file descriptor to "
+ "which a success code may be sent, all separated by "
+ "the '|' characters. This flag is specified if and only if the current "
+ "process is a sub-process launched for running a thread-safe "
+ "death test. FOR INTERNAL USE ONLY.");
+} // namespace internal
+
+#if GTEST_HAS_DEATH_TEST
+
+namespace internal {
+
+// Valid only for fast death tests. Indicates the code is running in the
+// child process of a fast style death test.
+static bool g_in_fast_death_test_child = false;
+
+// Returns a Boolean value indicating whether the caller is currently
+// executing in the context of the death test child process. Tools such as
+// Valgrind heap checkers may need this to modify their behavior in death
+// tests. IMPORTANT: This is an internal utility. Using it may break the
+// implementation of death tests. User code MUST NOT use it.
+bool InDeathTestChild() {
+# if GTEST_OS_WINDOWS
+
+ // On Windows, death tests are thread-safe regardless of the value of the
+ // death_test_style flag.
+ return !GTEST_FLAG(internal_run_death_test).empty();
+
+# else
+
+ if (GTEST_FLAG(death_test_style) == "threadsafe")
+ return !GTEST_FLAG(internal_run_death_test).empty();
+ else
+ return g_in_fast_death_test_child;
+#endif
+}
+
+} // namespace internal
+
+// ExitedWithCode constructor.
+ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) {
+}
+
+// ExitedWithCode function-call operator.
+bool ExitedWithCode::operator()(int exit_status) const {
+# if GTEST_OS_WINDOWS
+
+ return exit_status == exit_code_;
+
+# else
+
+ return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_;
+
+# endif // GTEST_OS_WINDOWS
+}
+
+# if !GTEST_OS_WINDOWS
+// KilledBySignal constructor.
+KilledBySignal::KilledBySignal(int signum) : signum_(signum) {
+}
+
+// KilledBySignal function-call operator.
+bool KilledBySignal::operator()(int exit_status) const {
+ return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_;
+}
+# endif // !GTEST_OS_WINDOWS
+
+namespace internal {
+
+// Utilities needed for death tests.
+
+// Generates a textual description of a given exit code, in the format
+// specified by wait(2).
+static std::string ExitSummary(int exit_code) {
+ Message m;
+
+# if GTEST_OS_WINDOWS
+
+ m << "Exited with exit status " << exit_code;
+
+# else
+
+ if (WIFEXITED(exit_code)) {
+ m << "Exited with exit status " << WEXITSTATUS(exit_code);
+ } else if (WIFSIGNALED(exit_code)) {
+ m << "Terminated by signal " << WTERMSIG(exit_code);
+ }
+# ifdef WCOREDUMP
+ if (WCOREDUMP(exit_code)) {
+ m << " (core dumped)";
+ }
+# endif
+# endif // GTEST_OS_WINDOWS
+
+ return m.GetString();
+}
+
+// Returns true if exit_status describes a process that was terminated
+// by a signal, or exited normally with a nonzero exit code.
+bool ExitedUnsuccessfully(int exit_status) {
+ return !ExitedWithCode(0)(exit_status);
+}
+
+# if !GTEST_OS_WINDOWS
+// Generates a textual failure message when a death test finds more than
+// one thread running, or cannot determine the number of threads, prior
+// to executing the given statement. It is the responsibility of the
+// caller not to pass a thread_count of 1.
+static std::string DeathTestThreadWarning(size_t thread_count) {
+ Message msg;
+ msg << "Death tests use fork(), which is unsafe particularly"
+ << " in a threaded context. For this test, " << GTEST_NAME_ << " ";
+ if (thread_count == 0)
+ msg << "couldn't detect the number of threads.";
+ else
+ msg << "detected " << thread_count << " threads.";
+ return msg.GetString();
+}
+# endif // !GTEST_OS_WINDOWS
+
+// Flag characters for reporting a death test that did not die.
+static const char kDeathTestLived = 'L';
+static const char kDeathTestReturned = 'R';
+static const char kDeathTestThrew = 'T';
+static const char kDeathTestInternalError = 'I';
+
+// An enumeration describing all of the possible ways that a death test can
+// conclude. DIED means that the process died while executing the test
+// code; LIVED means that process lived beyond the end of the test code;
+// RETURNED means that the test statement attempted to execute a return
+// statement, which is not allowed; THREW means that the test statement
+// returned control by throwing an exception. IN_PROGRESS means the test
+// has not yet concluded.
+// TODO(vladl@google.com): Unify names and possibly values for
+// AbortReason, DeathTestOutcome, and flag characters above.
+enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW };
+
+// Routine for aborting the program which is safe to call from an
+// exec-style death test child process, in which case the error
+// message is propagated back to the parent process. Otherwise, the
+// message is simply printed to stderr. In either case, the program
+// then exits with status 1.
+void DeathTestAbort(const std::string& message) {
+ // On a POSIX system, this function may be called from a threadsafe-style
+ // death test child process, which operates on a very small stack. Use
+ // the heap for any additional non-minuscule memory requirements.
+ const InternalRunDeathTestFlag* const flag =
+ GetUnitTestImpl()->internal_run_death_test_flag();
+ if (flag != NULL) {
+ FILE* parent = posix::FDOpen(flag->write_fd(), "w");
+ fputc(kDeathTestInternalError, parent);
+ fprintf(parent, "%s", message.c_str());
+ fflush(parent);
+ _exit(1);
+ } else {
+ fprintf(stderr, "%s", message.c_str());
+ fflush(stderr);
+ posix::Abort();
+ }
+}
+
+// A replacement for CHECK that calls DeathTestAbort if the assertion
+// fails.
+# define GTEST_DEATH_TEST_CHECK_(expression) \
+ do { \
+ if (!::testing::internal::IsTrue(expression)) { \
+ DeathTestAbort( \
+ ::std::string("CHECK failed: File ") + __FILE__ + ", line " \
+ + ::testing::internal::StreamableToString(__LINE__) + ": " \
+ + #expression); \
+ } \
+ } while (::testing::internal::AlwaysFalse())
+
+// This macro is similar to GTEST_DEATH_TEST_CHECK_, but it is meant for
+// evaluating any system call that fulfills two conditions: it must return
+// -1 on failure, and set errno to EINTR when it is interrupted and
+// should be tried again. The macro expands to a loop that repeatedly
+// evaluates the expression as long as it evaluates to -1 and sets
+// errno to EINTR. If the expression evaluates to -1 but errno is
+// something other than EINTR, DeathTestAbort is called.
+# define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \
+ do { \
+ int gtest_retval; \
+ do { \
+ gtest_retval = (expression); \
+ } while (gtest_retval == -1 && errno == EINTR); \
+ if (gtest_retval == -1) { \
+ DeathTestAbort( \
+ ::std::string("CHECK failed: File ") + __FILE__ + ", line " \
+ + ::testing::internal::StreamableToString(__LINE__) + ": " \
+ + #expression + " != -1"); \
+ } \
+ } while (::testing::internal::AlwaysFalse())
+
+// Returns the message describing the last system error in errno.
+std::string GetLastErrnoDescription() {
+ return errno == 0 ? "" : posix::StrError(errno);
+}
+
+// This is called from a death test parent process to read a failure
+// message from the death test child process and log it with the FATAL
+// severity. On Windows, the message is read from a pipe handle. On other
+// platforms, it is read from a file descriptor.
+static void FailFromInternalError(int fd) {
+ Message error;
+ char buffer[256];
+ int num_read;
+
+ do {
+ while ((num_read = posix::Read(fd, buffer, 255)) > 0) {
+ buffer[num_read] = '\0';
+ error << buffer;
+ }
+ } while (num_read == -1 && errno == EINTR);
+
+ if (num_read == 0) {
+ GTEST_LOG_(FATAL) << error.GetString();
+ } else {
+ const int last_error = errno;
+ GTEST_LOG_(FATAL) << "Error while reading death test internal: "
+ << GetLastErrnoDescription() << " [" << last_error << "]";
+ }
+}
+
+// Death test constructor. Increments the running death test count
+// for the current test.
+DeathTest::DeathTest() {
+ TestInfo* const info = GetUnitTestImpl()->current_test_info();
+ if (info == NULL) {
+ DeathTestAbort("Cannot run a death test outside of a TEST or "
+ "TEST_F construct");
+ }
+}
+
+// Creates and returns a death test by dispatching to the current
+// death test factory.
+bool DeathTest::Create(const char* statement, const RE* regex,
+ const char* file, int line, DeathTest** test) {
+ return GetUnitTestImpl()->death_test_factory()->Create(
+ statement, regex, file, line, test);
+}
+
+const char* DeathTest::LastMessage() {
+ return last_death_test_message_.c_str();
+}
+
+void DeathTest::set_last_death_test_message(const std::string& message) {
+ last_death_test_message_ = message;
+}
+
+std::string DeathTest::last_death_test_message_;
+
+// Provides cross platform implementation for some death functionality.
+class DeathTestImpl : public DeathTest {
+ protected:
+ DeathTestImpl(const char* a_statement, const RE* a_regex)
+ : statement_(a_statement),
+ regex_(a_regex),
+ spawned_(false),
+ status_(-1),
+ outcome_(IN_PROGRESS),
+ read_fd_(-1),
+ write_fd_(-1) {}
+
+ // read_fd_ is expected to be closed and cleared by a derived class.
+ ~DeathTestImpl() { GTEST_DEATH_TEST_CHECK_(read_fd_ == -1); }
+
+ void Abort(AbortReason reason);
+ virtual bool Passed(bool status_ok);
+
+ const char* statement() const { return statement_; }
+ const RE* regex() const { return regex_; }
+ bool spawned() const { return spawned_; }
+ void set_spawned(bool is_spawned) { spawned_ = is_spawned; }
+ int status() const { return status_; }
+ void set_status(int a_status) { status_ = a_status; }
+ DeathTestOutcome outcome() const { return outcome_; }
+ void set_outcome(DeathTestOutcome an_outcome) { outcome_ = an_outcome; }
+ int read_fd() const { return read_fd_; }
+ void set_read_fd(int fd) { read_fd_ = fd; }
+ int write_fd() const { return write_fd_; }
+ void set_write_fd(int fd) { write_fd_ = fd; }
+
+ // Called in the parent process only. Reads the result code of the death
+ // test child process via a pipe, interprets it to set the outcome_
+ // member, and closes read_fd_. Outputs diagnostics and terminates in
+ // case of unexpected codes.
+ void ReadAndInterpretStatusByte();
+
+ private:
+ // The textual content of the code this object is testing. This class
+ // doesn't own this string and should not attempt to delete it.
+ const char* const statement_;
+ // The regular expression which test output must match. DeathTestImpl
+ // doesn't own this object and should not attempt to delete it.
+ const RE* const regex_;
+ // True if the death test child process has been successfully spawned.
+ bool spawned_;
+ // The exit status of the child process.
+ int status_;
+ // How the death test concluded.
+ DeathTestOutcome outcome_;
+ // Descriptor to the read end of the pipe to the child process. It is
+ // always -1 in the child process. The child keeps its write end of the
+ // pipe in write_fd_.
+ int read_fd_;
+ // Descriptor to the child's write end of the pipe to the parent process.
+ // It is always -1 in the parent process. The parent keeps its end of the
+ // pipe in read_fd_.
+ int write_fd_;
+};
+
+// Called in the parent process only. Reads the result code of the death
+// test child process via a pipe, interprets it to set the outcome_
+// member, and closes read_fd_. Outputs diagnostics and terminates in
+// case of unexpected codes.
+void DeathTestImpl::ReadAndInterpretStatusByte() {
+ char flag;
+ int bytes_read;
+
+ // The read() here blocks until data is available (signifying the
+ // failure of the death test) or until the pipe is closed (signifying
+ // its success), so it's okay to call this in the parent before
+ // the child process has exited.
+ do {
+ bytes_read = posix::Read(read_fd(), &flag, 1);
+ } while (bytes_read == -1 && errno == EINTR);
+
+ if (bytes_read == 0) {
+ set_outcome(DIED);
+ } else if (bytes_read == 1) {
+ switch (flag) {
+ case kDeathTestReturned:
+ set_outcome(RETURNED);
+ break;
+ case kDeathTestThrew:
+ set_outcome(THREW);
+ break;
+ case kDeathTestLived:
+ set_outcome(LIVED);
+ break;
+ case kDeathTestInternalError:
+ FailFromInternalError(read_fd()); // Does not return.
+ break;
+ default:
+ GTEST_LOG_(FATAL) << "Death test child process reported "
+ << "unexpected status byte ("
+ << static_cast<unsigned int>(flag) << ")";
+ }
+ } else {
+ GTEST_LOG_(FATAL) << "Read from death test child process failed: "
+ << GetLastErrnoDescription();
+ }
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Close(read_fd()));
+ set_read_fd(-1);
+}
+
+// Signals that the death test code which should have exited, didn't.
+// Should be called only in a death test child process.
+// Writes a status byte to the child's status file descriptor, then
+// calls _exit(1).
+void DeathTestImpl::Abort(AbortReason reason) {
+ // The parent process considers the death test to be a failure if
+ // it finds any data in our pipe. So, here we write a single flag byte
+ // to the pipe, then exit.
+ const char status_ch =
+ reason == TEST_DID_NOT_DIE ? kDeathTestLived :
+ reason == TEST_THREW_EXCEPTION ? kDeathTestThrew : kDeathTestReturned;
+
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1));
+ // We are leaking the descriptor here because on some platforms (i.e.,
+ // when built as Windows DLL), destructors of global objects will still
+ // run after calling _exit(). On such systems, write_fd_ will be
+ // indirectly closed from the destructor of UnitTestImpl, causing double
+ // close if it is also closed here. On debug configurations, double close
+ // may assert. As there are no in-process buffers to flush here, we are
+ // relying on the OS to close the descriptor after the process terminates
+ // when the destructors are not run.
+ _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash)
+}
+
+// Returns an indented copy of stderr output for a death test.
+// This makes distinguishing death test output lines from regular log lines
+// much easier.
+static ::std::string FormatDeathTestOutput(const ::std::string& output) {
+ ::std::string ret;
+ for (size_t at = 0; ; ) {
+ const size_t line_end = output.find('\n', at);
+ ret += "[ DEATH ] ";
+ if (line_end == ::std::string::npos) {
+ ret += output.substr(at);
+ break;
+ }
+ ret += output.substr(at, line_end + 1 - at);
+ at = line_end + 1;
+ }
+ return ret;
+}
+
+// Assesses the success or failure of a death test, using both private
+// members which have previously been set, and one argument:
+//
+// Private data members:
+// outcome: An enumeration describing how the death test
+// concluded: DIED, LIVED, THREW, or RETURNED. The death test
+// fails in the latter three cases.
+// status: The exit status of the child process. On *nix, it is in the
+// in the format specified by wait(2). On Windows, this is the
+// value supplied to the ExitProcess() API or a numeric code
+// of the exception that terminated the program.
+// regex: A regular expression object to be applied to
+// the test's captured standard error output; the death test
+// fails if it does not match.
+//
+// Argument:
+// status_ok: true if exit_status is acceptable in the context of
+// this particular death test, which fails if it is false
+//
+// Returns true iff all of the above conditions are met. Otherwise, the
+// first failing condition, in the order given above, is the one that is
+// reported. Also sets the last death test message string.
+bool DeathTestImpl::Passed(bool status_ok) {
+ if (!spawned())
+ return false;
+
+ const std::string error_message = GetCapturedStderr();
+
+ bool success = false;
+ Message buffer;
+
+ buffer << "Death test: " << statement() << "\n";
+ switch (outcome()) {
+ case LIVED:
+ buffer << " Result: failed to die.\n"
+ << " Error msg:\n" << FormatDeathTestOutput(error_message);
+ break;
+ case THREW:
+ buffer << " Result: threw an exception.\n"
+ << " Error msg:\n" << FormatDeathTestOutput(error_message);
+ break;
+ case RETURNED:
+ buffer << " Result: illegal return in test statement.\n"
+ << " Error msg:\n" << FormatDeathTestOutput(error_message);
+ break;
+ case DIED:
+ if (status_ok) {
+ const bool matched = RE::PartialMatch(error_message.c_str(), *regex());
+ if (matched) {
+ success = true;
+ } else {
+ buffer << " Result: died but not with expected error.\n"
+ << " Expected: " << regex()->pattern() << "\n"
+ << "Actual msg:\n" << FormatDeathTestOutput(error_message);
+ }
+ } else {
+ buffer << " Result: died but not with expected exit code:\n"
+ << " " << ExitSummary(status()) << "\n"
+ << "Actual msg:\n" << FormatDeathTestOutput(error_message);
+ }
+ break;
+ case IN_PROGRESS:
+ default:
+ GTEST_LOG_(FATAL)
+ << "DeathTest::Passed somehow called before conclusion of test";
+ }
+
+ DeathTest::set_last_death_test_message(buffer.GetString());
+ return success;
+}
+
+# if GTEST_OS_WINDOWS
+// WindowsDeathTest implements death tests on Windows. Due to the
+// specifics of starting new processes on Windows, death tests there are
+// always threadsafe, and Google Test considers the
+// --gtest_death_test_style=fast setting to be equivalent to
+// --gtest_death_test_style=threadsafe there.
+//
+// A few implementation notes: Like the Linux version, the Windows
+// implementation uses pipes for child-to-parent communication. But due to
+// the specifics of pipes on Windows, some extra steps are required:
+//
+// 1. The parent creates a communication pipe and stores handles to both
+// ends of it.
+// 2. The parent starts the child and provides it with the information
+// necessary to acquire the handle to the write end of the pipe.
+// 3. The child acquires the write end of the pipe and signals the parent
+// using a Windows event.
+// 4. Now the parent can release the write end of the pipe on its side. If
+// this is done before step 3, the object's reference count goes down to
+// 0 and it is destroyed, preventing the child from acquiring it. The
+// parent now has to release it, or read operations on the read end of
+// the pipe will not return when the child terminates.
+// 5. The parent reads child's output through the pipe (outcome code and
+// any possible error messages) from the pipe, and its stderr and then
+// determines whether to fail the test.
+//
+// Note: to distinguish Win32 API calls from the local method and function
+// calls, the former are explicitly resolved in the global namespace.
+//
+class WindowsDeathTest : public DeathTestImpl {
+ public:
+ WindowsDeathTest(const char* a_statement,
+ const RE* a_regex,
+ const char* file,
+ int line)
+ : DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {}
+
+ // All of these virtual functions are inherited from DeathTest.
+ virtual int Wait();
+ virtual TestRole AssumeRole();
+
+ private:
+ // The name of the file in which the death test is located.
+ const char* const file_;
+ // The line number on which the death test is located.
+ const int line_;
+ // Handle to the write end of the pipe to the child process.
+ AutoHandle write_handle_;
+ // Child process handle.
+ AutoHandle child_handle_;
+ // Event the child process uses to signal the parent that it has
+ // acquired the handle to the write end of the pipe. After seeing this
+ // event the parent can release its own handles to make sure its
+ // ReadFile() calls return when the child terminates.
+ AutoHandle event_handle_;
+};
+
+// Waits for the child in a death test to exit, returning its exit
+// status, or 0 if no child process exists. As a side effect, sets the
+// outcome data member.
+int WindowsDeathTest::Wait() {
+ if (!spawned())
+ return 0;
+
+ // Wait until the child either signals that it has acquired the write end
+ // of the pipe or it dies.
+ const HANDLE wait_handles[2] = { child_handle_.Get(), event_handle_.Get() };
+ switch (::WaitForMultipleObjects(2,
+ wait_handles,
+ FALSE, // Waits for any of the handles.
+ INFINITE)) {
+ case WAIT_OBJECT_0:
+ case WAIT_OBJECT_0 + 1:
+ break;
+ default:
+ GTEST_DEATH_TEST_CHECK_(false); // Should not get here.
+ }
+
+ // The child has acquired the write end of the pipe or exited.
+ // We release the handle on our side and continue.
+ write_handle_.Reset();
+ event_handle_.Reset();
+
+ ReadAndInterpretStatusByte();
+
+ // Waits for the child process to exit if it haven't already. This
+ // returns immediately if the child has already exited, regardless of
+ // whether previous calls to WaitForMultipleObjects synchronized on this
+ // handle or not.
+ GTEST_DEATH_TEST_CHECK_(
+ WAIT_OBJECT_0 == ::WaitForSingleObject(child_handle_.Get(),
+ INFINITE));
+ DWORD status_code;
+ GTEST_DEATH_TEST_CHECK_(
+ ::GetExitCodeProcess(child_handle_.Get(), &status_code) != FALSE);
+ child_handle_.Reset();
+ set_status(static_cast<int>(status_code));
+ return status();
+}
+
+// The AssumeRole process for a Windows death test. It creates a child
+// process with the same executable as the current process to run the
+// death test. The child process is given the --gtest_filter and
+// --gtest_internal_run_death_test flags such that it knows to run the
+// current death test only.
+DeathTest::TestRole WindowsDeathTest::AssumeRole() {
+ const UnitTestImpl* const impl = GetUnitTestImpl();
+ const InternalRunDeathTestFlag* const flag =
+ impl->internal_run_death_test_flag();
+ const TestInfo* const info = impl->current_test_info();
+ const int death_test_index = info->result()->death_test_count();
+
+ if (flag != NULL) {
+ // ParseInternalRunDeathTestFlag() has performed all the necessary
+ // processing.
+ set_write_fd(flag->write_fd());
+ return EXECUTE_TEST;
+ }
+
+ // WindowsDeathTest uses an anonymous pipe to communicate results of
+ // a death test.
+ SECURITY_ATTRIBUTES handles_are_inheritable = {
+ sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
+ HANDLE read_handle, write_handle;
+ GTEST_DEATH_TEST_CHECK_(
+ ::CreatePipe(&read_handle, &write_handle, &handles_are_inheritable,
+ 0) // Default buffer size.
+ != FALSE);
+ set_read_fd(::_open_osfhandle(reinterpret_cast<intptr_t>(read_handle),
+ O_RDONLY));
+ write_handle_.Reset(write_handle);
+ event_handle_.Reset(::CreateEvent(
+ &handles_are_inheritable,
+ TRUE, // The event will automatically reset to non-signaled state.
+ FALSE, // The initial state is non-signalled.
+ NULL)); // The even is unnamed.
+ GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != NULL);
+ const std::string filter_flag =
+ std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" +
+ info->test_case_name() + "." + info->name();
+ const std::string internal_flag =
+ std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag +
+ "=" + file_ + "|" + StreamableToString(line_) + "|" +
+ StreamableToString(death_test_index) + "|" +
+ StreamableToString(static_cast<unsigned int>(::GetCurrentProcessId())) +
+ // size_t has the same width as pointers on both 32-bit and 64-bit
+ // Windows platforms.
+ // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx.
+ "|" + StreamableToString(reinterpret_cast<size_t>(write_handle)) +
+ "|" + StreamableToString(reinterpret_cast<size_t>(event_handle_.Get()));
+
+ char executable_path[_MAX_PATH + 1]; // NOLINT
+ GTEST_DEATH_TEST_CHECK_(
+ _MAX_PATH + 1 != ::GetModuleFileNameA(NULL,
+ executable_path,
+ _MAX_PATH));
+
+ std::string command_line =
+ std::string(::GetCommandLineA()) + " " + filter_flag + " \"" +
+ internal_flag + "\"";
+
+ DeathTest::set_last_death_test_message("");
+
+ CaptureStderr();
+ // Flush the log buffers since the log streams are shared with the child.
+ FlushInfoLog();
+
+ // The child process will share the standard handles with the parent.
+ STARTUPINFOA startup_info;
+ memset(&startup_info, 0, sizeof(STARTUPINFO));
+ startup_info.dwFlags = STARTF_USESTDHANDLES;
+ startup_info.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE);
+ startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
+ startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
+
+ PROCESS_INFORMATION process_info;
+ GTEST_DEATH_TEST_CHECK_(::CreateProcessA(
+ executable_path,
+ const_cast<char*>(command_line.c_str()),
+ NULL, // Retuned process handle is not inheritable.
+ NULL, // Retuned thread handle is not inheritable.
+ TRUE, // Child inherits all inheritable handles (for write_handle_).
+ 0x0, // Default creation flags.
+ NULL, // Inherit the parent's environment.
+ UnitTest::GetInstance()->original_working_dir(),
+ &startup_info,
+ &process_info) != FALSE);
+ child_handle_.Reset(process_info.hProcess);
+ ::CloseHandle(process_info.hThread);
+ set_spawned(true);
+ return OVERSEE_TEST;
+}
+# else // We are not on Windows.
+
+// ForkingDeathTest provides implementations for most of the abstract
+// methods of the DeathTest interface. Only the AssumeRole method is
+// left undefined.
+class ForkingDeathTest : public DeathTestImpl {
+ public:
+ ForkingDeathTest(const char* statement, const RE* regex);
+
+ // All of these virtual functions are inherited from DeathTest.
+ virtual int Wait();
+
+ protected:
+ void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; }
+
+ private:
+ // PID of child process during death test; 0 in the child process itself.
+ pid_t child_pid_;
+};
+
+// Constructs a ForkingDeathTest.
+ForkingDeathTest::ForkingDeathTest(const char* a_statement, const RE* a_regex)
+ : DeathTestImpl(a_statement, a_regex),
+ child_pid_(-1) {}
+
+// Waits for the child in a death test to exit, returning its exit
+// status, or 0 if no child process exists. As a side effect, sets the
+// outcome data member.
+int ForkingDeathTest::Wait() {
+ if (!spawned())
+ return 0;
+
+ ReadAndInterpretStatusByte();
+
+ int status_value;
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(waitpid(child_pid_, &status_value, 0));
+ set_status(status_value);
+ return status_value;
+}
+
+// A concrete death test class that forks, then immediately runs the test
+// in the child process.
+class NoExecDeathTest : public ForkingDeathTest {
+ public:
+ NoExecDeathTest(const char* a_statement, const RE* a_regex) :
+ ForkingDeathTest(a_statement, a_regex) { }
+ virtual TestRole AssumeRole();
+};
+
+// The AssumeRole process for a fork-and-run death test. It implements a
+// straightforward fork, with a simple pipe to transmit the status byte.
+DeathTest::TestRole NoExecDeathTest::AssumeRole() {
+ const size_t thread_count = GetThreadCount();
+ if (thread_count != 1) {
+ GTEST_LOG_(WARNING) << DeathTestThreadWarning(thread_count);
+ }
+
+ int pipe_fd[2];
+ GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1);
+
+ DeathTest::set_last_death_test_message("");
+ CaptureStderr();
+ // When we fork the process below, the log file buffers are copied, but the
+ // file descriptors are shared. We flush all log files here so that closing
+ // the file descriptors in the child process doesn't throw off the
+ // synchronization between descriptors and buffers in the parent process.
+ // This is as close to the fork as possible to avoid a race condition in case
+ // there are multiple threads running before the death test, and another
+ // thread writes to the log file.
+ FlushInfoLog();
+
+ const pid_t child_pid = fork();
+ GTEST_DEATH_TEST_CHECK_(child_pid != -1);
+ set_child_pid(child_pid);
+ if (child_pid == 0) {
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[0]));
+ set_write_fd(pipe_fd[1]);
+ // Redirects all logging to stderr in the child process to prevent
+ // concurrent writes to the log files. We capture stderr in the parent
+ // process and append the child process' output to a log.
+ LogToStderr();
+ // Event forwarding to the listeners of event listener API mush be shut
+ // down in death test subprocesses.
+ GetUnitTestImpl()->listeners()->SuppressEventForwarding();
+ g_in_fast_death_test_child = true;
+ return EXECUTE_TEST;
+ } else {
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1]));
+ set_read_fd(pipe_fd[0]);
+ set_spawned(true);
+ return OVERSEE_TEST;
+ }
+}
+
+// A concrete death test class that forks and re-executes the main
+// program from the beginning, with command-line flags set that cause
+// only this specific death test to be run.
+class ExecDeathTest : public ForkingDeathTest {
+ public:
+ ExecDeathTest(const char* a_statement, const RE* a_regex,
+ const char* file, int line) :
+ ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { }
+ virtual TestRole AssumeRole();
+ private:
+ static ::std::vector<testing::internal::string>
+ GetArgvsForDeathTestChildProcess() {
+ ::std::vector<testing::internal::string> args = GetInjectableArgvs();
+ return args;
+ }
+ // The name of the file in which the death test is located.
+ const char* const file_;
+ // The line number on which the death test is located.
+ const int line_;
+};
+
+// Utility class for accumulating command-line arguments.
+class Arguments {
+ public:
+ Arguments() {
+ args_.push_back(NULL);
+ }
+
+ ~Arguments() {
+ for (std::vector<char*>::iterator i = args_.begin(); i != args_.end();
+ ++i) {
+ free(*i);
+ }
+ }
+ void AddArgument(const char* argument) {
+ args_.insert(args_.end() - 1, posix::StrDup(argument));
+ }
+
+ template <typename Str>
+ void AddArguments(const ::std::vector<Str>& arguments) {
+ for (typename ::std::vector<Str>::const_iterator i = arguments.begin();
+ i != arguments.end();
+ ++i) {
+ args_.insert(args_.end() - 1, posix::StrDup(i->c_str()));
+ }
+ }
+ char* const* Argv() {
+ return &args_[0];
+ }
+
+ private:
+ std::vector<char*> args_;
+};
+
+// A struct that encompasses the arguments to the child process of a
+// threadsafe-style death test process.
+struct ExecDeathTestArgs {
+ char* const* argv; // Command-line arguments for the child's call to exec
+ int close_fd; // File descriptor to close; the read end of a pipe
+};
+
+# if GTEST_OS_MAC
+inline char** GetEnviron() {
+ // When Google Test is built as a framework on MacOS X, the environ variable
+ // is unavailable. Apple's documentation (man environ) recommends using
+ // _NSGetEnviron() instead.
+ return *_NSGetEnviron();
+}
+# else
+// Some POSIX platforms expect you to declare environ. extern "C" makes
+// it reside in the global namespace.
+extern "C" char** environ;
+inline char** GetEnviron() { return environ; }
+# endif // GTEST_OS_MAC
+
+# if !GTEST_OS_QNX
+// The main function for a threadsafe-style death test child process.
+// This function is called in a clone()-ed process and thus must avoid
+// any potentially unsafe operations like malloc or libc functions.
+static int ExecDeathTestChildMain(void* child_arg) {
+ ExecDeathTestArgs* const args = static_cast<ExecDeathTestArgs*>(child_arg);
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd));
+
+ // We need to execute the test program in the same environment where
+ // it was originally invoked. Therefore we change to the original
+ // working directory first.
+ const char* const original_dir =
+ UnitTest::GetInstance()->original_working_dir();
+ // We can safely call chdir() as it's a direct system call.
+ if (chdir(original_dir) != 0) {
+ DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " +
+ GetLastErrnoDescription());
+ return EXIT_FAILURE;
+ }
+
+ // We can safely call execve() as it's a direct system call. We
+ // cannot use execvp() as it's a libc function and thus potentially
+ // unsafe. Since execve() doesn't search the PATH, the user must
+ // invoke the test program via a valid path that contains at least
+ // one path separator.
+ execve(args->argv[0], args->argv, GetEnviron());
+ DeathTestAbort(std::string("execve(") + args->argv[0] + ", ...) in " +
+ original_dir + " failed: " +
+ GetLastErrnoDescription());
+ return EXIT_FAILURE;
+}
+# endif // !GTEST_OS_QNX
+
+// Two utility routines that together determine the direction the stack
+// grows.
+// This could be accomplished more elegantly by a single recursive
+// function, but we want to guard against the unlikely possibility of
+// a smart compiler optimizing the recursion away.
+//
+// GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining
+// StackLowerThanAddress into StackGrowsDown, which then doesn't give
+// correct answer.
+void StackLowerThanAddress(const void* ptr, bool* result) GTEST_NO_INLINE_;
+void StackLowerThanAddress(const void* ptr, bool* result) {
+ int dummy;
+ *result = (&dummy < ptr);
+}
+
+bool StackGrowsDown() {
+ int dummy;
+ bool result;
+ StackLowerThanAddress(&dummy, &result);
+ return result;
+}
+
+// Spawns a child process with the same executable as the current process in
+// a thread-safe manner and instructs it to run the death test. The
+// implementation uses fork(2) + exec. On systems where clone(2) is
+// available, it is used instead, being slightly more thread-safe. On QNX,
+// fork supports only single-threaded environments, so this function uses
+// spawn(2) there instead. The function dies with an error message if
+// anything goes wrong.
+static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) {
+ ExecDeathTestArgs args = { argv, close_fd };
+ pid_t child_pid = -1;
+
+# if GTEST_OS_QNX
+ // Obtains the current directory and sets it to be closed in the child
+ // process.
+ const int cwd_fd = open(".", O_RDONLY);
+ GTEST_DEATH_TEST_CHECK_(cwd_fd != -1);
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(cwd_fd, F_SETFD, FD_CLOEXEC));
+ // We need to execute the test program in the same environment where
+ // it was originally invoked. Therefore we change to the original
+ // working directory first.
+ const char* const original_dir =
+ UnitTest::GetInstance()->original_working_dir();
+ // We can safely call chdir() as it's a direct system call.
+ if (chdir(original_dir) != 0) {
+ DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " +
+ GetLastErrnoDescription());
+ return EXIT_FAILURE;
+ }
+
+ int fd_flags;
+ // Set close_fd to be closed after spawn.
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(fd_flags = fcntl(close_fd, F_GETFD));
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(close_fd, F_SETFD,
+ fd_flags | FD_CLOEXEC));
+ struct inheritance inherit = {0};
+ // spawn is a system call.
+ child_pid = spawn(args.argv[0], 0, NULL, &inherit, args.argv, GetEnviron());
+ // Restores the current working directory.
+ GTEST_DEATH_TEST_CHECK_(fchdir(cwd_fd) != -1);
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd));
+
+# else // GTEST_OS_QNX
+# if GTEST_OS_LINUX
+ // When a SIGPROF signal is received while fork() or clone() are executing,
+ // the process may hang. To avoid this, we ignore SIGPROF here and re-enable
+ // it after the call to fork()/clone() is complete.
+ struct sigaction saved_sigprof_action;
+ struct sigaction ignore_sigprof_action;
+ memset(&ignore_sigprof_action, 0, sizeof(ignore_sigprof_action));
+ sigemptyset(&ignore_sigprof_action.sa_mask);
+ ignore_sigprof_action.sa_handler = SIG_IGN;
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(sigaction(
+ SIGPROF, &ignore_sigprof_action, &saved_sigprof_action));
+# endif // GTEST_OS_LINUX
+
+# if GTEST_HAS_CLONE
+ const bool use_fork = GTEST_FLAG(death_test_use_fork);
+
+ if (!use_fork) {
+ static const bool stack_grows_down = StackGrowsDown();
+ const size_t stack_size = getpagesize();
+ // MMAP_ANONYMOUS is not defined on Mac, so we use MAP_ANON instead.
+ void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE, -1, 0);
+ GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED);
+
+ // Maximum stack alignment in bytes: For a downward-growing stack, this
+ // amount is subtracted from size of the stack space to get an address
+ // that is within the stack space and is aligned on all systems we care
+ // about. As far as I know there is no ABI with stack alignment greater
+ // than 64. We assume stack and stack_size already have alignment of
+ // kMaxStackAlignment.
+ const size_t kMaxStackAlignment = 64;
+ void* const stack_top =
+ static_cast<char*>(stack) +
+ (stack_grows_down ? stack_size - kMaxStackAlignment : 0);
+ GTEST_DEATH_TEST_CHECK_(stack_size > kMaxStackAlignment &&
+ reinterpret_cast<intptr_t>(stack_top) % kMaxStackAlignment == 0);
+
+ child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args);
+
+ GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1);
+ }
+# else
+ const bool use_fork = true;
+# endif // GTEST_HAS_CLONE
+
+ if (use_fork && (child_pid = fork()) == 0) {
+ ExecDeathTestChildMain(&args);
+ _exit(0);
+ }
+# endif // GTEST_OS_QNX
+# if GTEST_OS_LINUX
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(
+ sigaction(SIGPROF, &saved_sigprof_action, NULL));
+# endif // GTEST_OS_LINUX
+
+ GTEST_DEATH_TEST_CHECK_(child_pid != -1);
+ return child_pid;
+}
+
+// The AssumeRole process for a fork-and-exec death test. It re-executes the
+// main program from the beginning, setting the --gtest_filter
+// and --gtest_internal_run_death_test flags to cause only the current
+// death test to be re-run.
+DeathTest::TestRole ExecDeathTest::AssumeRole() {
+ const UnitTestImpl* const impl = GetUnitTestImpl();
+ const InternalRunDeathTestFlag* const flag =
+ impl->internal_run_death_test_flag();
+ const TestInfo* const info = impl->current_test_info();
+ const int death_test_index = info->result()->death_test_count();
+
+ if (flag != NULL) {
+ set_write_fd(flag->write_fd());
+ return EXECUTE_TEST;
+ }
+
+ int pipe_fd[2];
+ GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1);
+ // Clear the close-on-exec flag on the write end of the pipe, lest
+ // it be closed when the child process does an exec:
+ GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1);
+
+ const std::string filter_flag =
+ std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "="
+ + info->test_case_name() + "." + info->name();
+ const std::string internal_flag =
+ std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "="
+ + file_ + "|" + StreamableToString(line_) + "|"
+ + StreamableToString(death_test_index) + "|"
+ + StreamableToString(pipe_fd[1]);
+ Arguments args;
+ args.AddArguments(GetArgvsForDeathTestChildProcess());
+ args.AddArgument(filter_flag.c_str());
+ args.AddArgument(internal_flag.c_str());
+
+ DeathTest::set_last_death_test_message("");
+
+ CaptureStderr();
+ // See the comment in NoExecDeathTest::AssumeRole for why the next line
+ // is necessary.
+ FlushInfoLog();
+
+ const pid_t child_pid = ExecDeathTestSpawnChild(args.Argv(), pipe_fd[0]);
+ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1]));
+ set_child_pid(child_pid);
+ set_read_fd(pipe_fd[0]);
+ set_spawned(true);
+ return OVERSEE_TEST;
+}
+
+# endif // !GTEST_OS_WINDOWS
+
+// Creates a concrete DeathTest-derived class that depends on the
+// --gtest_death_test_style flag, and sets the pointer pointed to
+// by the "test" argument to its address. If the test should be
+// skipped, sets that pointer to NULL. Returns true, unless the
+// flag is set to an invalid value.
+bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex,
+ const char* file, int line,
+ DeathTest** test) {
+ UnitTestImpl* const impl = GetUnitTestImpl();
+ const InternalRunDeathTestFlag* const flag =
+ impl->internal_run_death_test_flag();
+ const int death_test_index = impl->current_test_info()
+ ->increment_death_test_count();
+
+ if (flag != NULL) {
+ if (death_test_index > flag->index()) {
+ DeathTest::set_last_death_test_message(
+ "Death test count (" + StreamableToString(death_test_index)
+ + ") somehow exceeded expected maximum ("
+ + StreamableToString(flag->index()) + ")");
+ return false;
+ }
+
+ if (!(flag->file() == file && flag->line() == line &&
+ flag->index() == death_test_index)) {
+ *test = NULL;
+ return true;
+ }
+ }
+
+# if GTEST_OS_WINDOWS
+
+ if (GTEST_FLAG(death_test_style) == "threadsafe" ||
+ GTEST_FLAG(death_test_style) == "fast") {
+ *test = new WindowsDeathTest(statement, regex, file, line);
+ }
+
+# else
+
+ if (GTEST_FLAG(death_test_style) == "threadsafe") {
+ *test = new ExecDeathTest(statement, regex, file, line);
+ } else if (GTEST_FLAG(death_test_style) == "fast") {
+ *test = new NoExecDeathTest(statement, regex);
+ }
+
+# endif // GTEST_OS_WINDOWS
+
+ else { // NOLINT - this is more readable than unbalanced brackets inside #if.
+ DeathTest::set_last_death_test_message(
+ "Unknown death test style \"" + GTEST_FLAG(death_test_style)
+ + "\" encountered");
+ return false;
+ }
+
+ return true;
+}
+
+// Splits a given string on a given delimiter, populating a given
+// vector with the fields. GTEST_HAS_DEATH_TEST implies that we have
+// ::std::string, so we can use it here.
+static void SplitString(const ::std::string& str, char delimiter,
+ ::std::vector< ::std::string>* dest) {
+ ::std::vector< ::std::string> parsed;
+ ::std::string::size_type pos = 0;
+ while (::testing::internal::AlwaysTrue()) {
+ const ::std::string::size_type colon = str.find(delimiter, pos);
+ if (colon == ::std::string::npos) {
+ parsed.push_back(str.substr(pos));
+ break;
+ } else {
+ parsed.push_back(str.substr(pos, colon - pos));
+ pos = colon + 1;
+ }
+ }
+ dest->swap(parsed);
+}
+
+# if GTEST_OS_WINDOWS
+// Recreates the pipe and event handles from the provided parameters,
+// signals the event, and returns a file descriptor wrapped around the pipe
+// handle. This function is called in the child process only.
+int GetStatusFileDescriptor(unsigned int parent_process_id,
+ size_t write_handle_as_size_t,
+ size_t event_handle_as_size_t) {
+ AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE,
+ FALSE, // Non-inheritable.
+ parent_process_id));
+ if (parent_process_handle.Get() == INVALID_HANDLE_VALUE) {
+ DeathTestAbort("Unable to open parent process " +
+ StreamableToString(parent_process_id));
+ }
+
+ // TODO(vladl@google.com): Replace the following check with a
+ // compile-time assertion when available.
+ GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t));
+
+ const HANDLE write_handle =
+ reinterpret_cast<HANDLE>(write_handle_as_size_t);
+ HANDLE dup_write_handle;
+
+ // The newly initialized handle is accessible only in in the parent
+ // process. To obtain one accessible within the child, we need to use
+ // DuplicateHandle.
+ if (!::DuplicateHandle(parent_process_handle.Get(), write_handle,
+ ::GetCurrentProcess(), &dup_write_handle,
+ 0x0, // Requested privileges ignored since
+ // DUPLICATE_SAME_ACCESS is used.
+ FALSE, // Request non-inheritable handler.
+ DUPLICATE_SAME_ACCESS)) {
+ DeathTestAbort("Unable to duplicate the pipe handle " +
+ StreamableToString(write_handle_as_size_t) +
+ " from the parent process " +
+ StreamableToString(parent_process_id));
+ }
+
+ const HANDLE event_handle = reinterpret_cast<HANDLE>(event_handle_as_size_t);
+ HANDLE dup_event_handle;
+
+ if (!::DuplicateHandle(parent_process_handle.Get(), event_handle,
+ ::GetCurrentProcess(), &dup_event_handle,
+ 0x0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS)) {
+ DeathTestAbort("Unable to duplicate the event handle " +
+ StreamableToString(event_handle_as_size_t) +
+ " from the parent process " +
+ StreamableToString(parent_process_id));
+ }
+
+ const int write_fd =
+ ::_open_osfhandle(reinterpret_cast<intptr_t>(dup_write_handle), O_APPEND);
+ if (write_fd == -1) {
+ DeathTestAbort("Unable to convert pipe handle " +
+ StreamableToString(write_handle_as_size_t) +
+ " to a file descriptor");
+ }
+
+ // Signals the parent that the write end of the pipe has been acquired
+ // so the parent can release its own write end.
+ ::SetEvent(dup_event_handle);
+
+ return write_fd;
+}
+# endif // GTEST_OS_WINDOWS
+
+// Returns a newly created InternalRunDeathTestFlag object with fields
+// initialized from the GTEST_FLAG(internal_run_death_test) flag if
+// the flag is specified; otherwise returns NULL.
+InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() {
+ if (GTEST_FLAG(internal_run_death_test) == "") return NULL;
+
+ // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we
+ // can use it here.
+ int line = -1;
+ int index = -1;
+ ::std::vector< ::std::string> fields;
+ SplitString(GTEST_FLAG(internal_run_death_test).c_str(), '|', &fields);
+ int write_fd = -1;
+
+# if GTEST_OS_WINDOWS
+
+ unsigned int parent_process_id = 0;
+ size_t write_handle_as_size_t = 0;
+ size_t event_handle_as_size_t = 0;
+
+ if (fields.size() != 6
+ || !ParseNaturalNumber(fields[1], &line)
+ || !ParseNaturalNumber(fields[2], &index)
+ || !ParseNaturalNumber(fields[3], &parent_process_id)
+ || !ParseNaturalNumber(fields[4], &write_handle_as_size_t)
+ || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) {
+ DeathTestAbort("Bad --gtest_internal_run_death_test flag: " +
+ GTEST_FLAG(internal_run_death_test));
+ }
+ write_fd = GetStatusFileDescriptor(parent_process_id,
+ write_handle_as_size_t,
+ event_handle_as_size_t);
+# else
+
+ if (fields.size() != 4
+ || !ParseNaturalNumber(fields[1], &line)
+ || !ParseNaturalNumber(fields[2], &index)
+ || !ParseNaturalNumber(fields[3], &write_fd)) {
+ DeathTestAbort("Bad --gtest_internal_run_death_test flag: "
+ + GTEST_FLAG(internal_run_death_test));
+ }
+
+# endif // GTEST_OS_WINDOWS
+
+ return new InternalRunDeathTestFlag(fields[0], line, index, write_fd);
+}
+
+} // namespace internal
+
+#endif // GTEST_HAS_DEATH_TEST
+
+} // namespace testing
diff --git a/extern/gtest/src/gtest-filepath.cc b/extern/gtest/src/gtest-filepath.cc
new file mode 100644
index 00000000000..6be58b6fca2
--- /dev/null
+++ b/extern/gtest/src/gtest-filepath.cc
@@ -0,0 +1,382 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: keith.ray@gmail.com (Keith Ray)
+
+#include "gtest/gtest-message.h"
+#include "gtest/internal/gtest-filepath.h"
+#include "gtest/internal/gtest-port.h"
+
+#include <stdlib.h>
+
+#if GTEST_OS_WINDOWS_MOBILE
+# include <windows.h>
+#elif GTEST_OS_WINDOWS
+# include <direct.h>
+# include <io.h>
+#elif GTEST_OS_SYMBIAN
+// Symbian OpenC has PATH_MAX in sys/syslimits.h
+# include <sys/syslimits.h>
+#else
+# include <limits.h>
+# include <climits> // Some Linux distributions define PATH_MAX here.
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+#if GTEST_OS_WINDOWS
+# define GTEST_PATH_MAX_ _MAX_PATH
+#elif defined(PATH_MAX)
+# define GTEST_PATH_MAX_ PATH_MAX
+#elif defined(_XOPEN_PATH_MAX)
+# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX
+#else
+# define GTEST_PATH_MAX_ _POSIX_PATH_MAX
+#endif // GTEST_OS_WINDOWS
+
+#include "gtest/internal/gtest-string.h"
+
+namespace testing {
+namespace internal {
+
+#if GTEST_OS_WINDOWS
+// On Windows, '\\' is the standard path separator, but many tools and the
+// Windows API also accept '/' as an alternate path separator. Unless otherwise
+// noted, a file path can contain either kind of path separators, or a mixture
+// of them.
+const char kPathSeparator = '\\';
+const char kAlternatePathSeparator = '/';
+const char kPathSeparatorString[] = "\\";
+const char kAlternatePathSeparatorString[] = "/";
+# if GTEST_OS_WINDOWS_MOBILE
+// Windows CE doesn't have a current directory. You should not use
+// the current directory in tests on Windows CE, but this at least
+// provides a reasonable fallback.
+const char kCurrentDirectoryString[] = "\\";
+// Windows CE doesn't define INVALID_FILE_ATTRIBUTES
+const DWORD kInvalidFileAttributes = 0xffffffff;
+# else
+const char kCurrentDirectoryString[] = ".\\";
+# endif // GTEST_OS_WINDOWS_MOBILE
+#else
+const char kPathSeparator = '/';
+const char kPathSeparatorString[] = "/";
+const char kCurrentDirectoryString[] = "./";
+#endif // GTEST_OS_WINDOWS
+
+// Returns whether the given character is a valid path separator.
+static bool IsPathSeparator(char c) {
+#if GTEST_HAS_ALT_PATH_SEP_
+ return (c == kPathSeparator) || (c == kAlternatePathSeparator);
+#else
+ return c == kPathSeparator;
+#endif
+}
+
+// Returns the current working directory, or "" if unsuccessful.
+FilePath FilePath::GetCurrentDir() {
+#if GTEST_OS_WINDOWS_MOBILE
+ // Windows CE doesn't have a current directory, so we just return
+ // something reasonable.
+ return FilePath(kCurrentDirectoryString);
+#elif GTEST_OS_WINDOWS
+ char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
+ return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
+#else
+ char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
+ return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
+#endif // GTEST_OS_WINDOWS_MOBILE
+}
+
+// Returns a copy of the FilePath with the case-insensitive extension removed.
+// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
+// FilePath("dir/file"). If a case-insensitive extension is not
+// found, returns a copy of the original FilePath.
+FilePath FilePath::RemoveExtension(const char* extension) const {
+ const std::string dot_extension = std::string(".") + extension;
+ if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) {
+ return FilePath(pathname_.substr(
+ 0, pathname_.length() - dot_extension.length()));
+ }
+ return *this;
+}
+
+// Returns a pointer to the last occurence of a valid path separator in
+// the FilePath. On Windows, for example, both '/' and '\' are valid path
+// separators. Returns NULL if no path separator was found.
+const char* FilePath::FindLastPathSeparator() const {
+ const char* const last_sep = strrchr(c_str(), kPathSeparator);
+#if GTEST_HAS_ALT_PATH_SEP_
+ const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator);
+ // Comparing two pointers of which only one is NULL is undefined.
+ if (last_alt_sep != NULL &&
+ (last_sep == NULL || last_alt_sep > last_sep)) {
+ return last_alt_sep;
+ }
+#endif
+ return last_sep;
+}
+
+// Returns a copy of the FilePath with the directory part removed.
+// Example: FilePath("path/to/file").RemoveDirectoryName() returns
+// FilePath("file"). If there is no directory part ("just_a_file"), it returns
+// the FilePath unmodified. If there is no file part ("just_a_dir/") it
+// returns an empty FilePath ("").
+// On Windows platform, '\' is the path separator, otherwise it is '/'.
+FilePath FilePath::RemoveDirectoryName() const {
+ const char* const last_sep = FindLastPathSeparator();
+ return last_sep ? FilePath(last_sep + 1) : *this;
+}
+
+// RemoveFileName returns the directory path with the filename removed.
+// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
+// If the FilePath is "a_file" or "/a_file", RemoveFileName returns
+// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
+// not have a file, like "just/a/dir/", it returns the FilePath unmodified.
+// On Windows platform, '\' is the path separator, otherwise it is '/'.
+FilePath FilePath::RemoveFileName() const {
+ const char* const last_sep = FindLastPathSeparator();
+ std::string dir;
+ if (last_sep) {
+ dir = std::string(c_str(), last_sep + 1 - c_str());
+ } else {
+ dir = kCurrentDirectoryString;
+ }
+ return FilePath(dir);
+}
+
+// Helper functions for naming files in a directory for xml output.
+
+// Given directory = "dir", base_name = "test", number = 0,
+// extension = "xml", returns "dir/test.xml". If number is greater
+// than zero (e.g., 12), returns "dir/test_12.xml".
+// On Windows platform, uses \ as the separator rather than /.
+FilePath FilePath::MakeFileName(const FilePath& directory,
+ const FilePath& base_name,
+ int number,
+ const char* extension) {
+ std::string file;
+ if (number == 0) {
+ file = base_name.string() + "." + extension;
+ } else {
+ file = base_name.string() + "_" + StreamableToString(number)
+ + "." + extension;
+ }
+ return ConcatPaths(directory, FilePath(file));
+}
+
+// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml".
+// On Windows, uses \ as the separator rather than /.
+FilePath FilePath::ConcatPaths(const FilePath& directory,
+ const FilePath& relative_path) {
+ if (directory.IsEmpty())
+ return relative_path;
+ const FilePath dir(directory.RemoveTrailingPathSeparator());
+ return FilePath(dir.string() + kPathSeparator + relative_path.string());
+}
+
+// Returns true if pathname describes something findable in the file-system,
+// either a file, directory, or whatever.
+bool FilePath::FileOrDirectoryExists() const {
+#if GTEST_OS_WINDOWS_MOBILE
+ LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str());
+ const DWORD attributes = GetFileAttributes(unicode);
+ delete [] unicode;
+ return attributes != kInvalidFileAttributes;
+#else
+ posix::StatStruct file_stat;
+ return posix::Stat(pathname_.c_str(), &file_stat) == 0;
+#endif // GTEST_OS_WINDOWS_MOBILE
+}
+
+// Returns true if pathname describes a directory in the file-system
+// that exists.
+bool FilePath::DirectoryExists() const {
+ bool result = false;
+#if GTEST_OS_WINDOWS
+ // Don't strip off trailing separator if path is a root directory on
+ // Windows (like "C:\\").
+ const FilePath& path(IsRootDirectory() ? *this :
+ RemoveTrailingPathSeparator());
+#else
+ const FilePath& path(*this);
+#endif
+
+#if GTEST_OS_WINDOWS_MOBILE
+ LPCWSTR unicode = String::AnsiToUtf16(path.c_str());
+ const DWORD attributes = GetFileAttributes(unicode);
+ delete [] unicode;
+ if ((attributes != kInvalidFileAttributes) &&
+ (attributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ result = true;
+ }
+#else
+ posix::StatStruct file_stat;
+ result = posix::Stat(path.c_str(), &file_stat) == 0 &&
+ posix::IsDir(file_stat);
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+ return result;
+}
+
+// Returns true if pathname describes a root directory. (Windows has one
+// root directory per disk drive.)
+bool FilePath::IsRootDirectory() const {
+#if GTEST_OS_WINDOWS
+ // TODO(wan@google.com): on Windows a network share like
+ // \\server\share can be a root directory, although it cannot be the
+ // current directory. Handle this properly.
+ return pathname_.length() == 3 && IsAbsolutePath();
+#else
+ return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]);
+#endif
+}
+
+// Returns true if pathname describes an absolute path.
+bool FilePath::IsAbsolutePath() const {
+ const char* const name = pathname_.c_str();
+#if GTEST_OS_WINDOWS
+ return pathname_.length() >= 3 &&
+ ((name[0] >= 'a' && name[0] <= 'z') ||
+ (name[0] >= 'A' && name[0] <= 'Z')) &&
+ name[1] == ':' &&
+ IsPathSeparator(name[2]);
+#else
+ return IsPathSeparator(name[0]);
+#endif
+}
+
+// Returns a pathname for a file that does not currently exist. The pathname
+// will be directory/base_name.extension or
+// directory/base_name_<number>.extension if directory/base_name.extension
+// already exists. The number will be incremented until a pathname is found
+// that does not already exist.
+// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
+// There could be a race condition if two or more processes are calling this
+// function at the same time -- they could both pick the same filename.
+FilePath FilePath::GenerateUniqueFileName(const FilePath& directory,
+ const FilePath& base_name,
+ const char* extension) {
+ FilePath full_pathname;
+ int number = 0;
+ do {
+ full_pathname.Set(MakeFileName(directory, base_name, number++, extension));
+ } while (full_pathname.FileOrDirectoryExists());
+ return full_pathname;
+}
+
+// Returns true if FilePath ends with a path separator, which indicates that
+// it is intended to represent a directory. Returns false otherwise.
+// This does NOT check that a directory (or file) actually exists.
+bool FilePath::IsDirectory() const {
+ return !pathname_.empty() &&
+ IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]);
+}
+
+// Create directories so that path exists. Returns true if successful or if
+// the directories already exist; returns false if unable to create directories
+// for any reason.
+bool FilePath::CreateDirectoriesRecursively() const {
+ if (!this->IsDirectory()) {
+ return false;
+ }
+
+ if (pathname_.length() == 0 || this->DirectoryExists()) {
+ return true;
+ }
+
+ const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName());
+ return parent.CreateDirectoriesRecursively() && this->CreateFolder();
+}
+
+// Create the directory so that path exists. Returns true if successful or
+// if the directory already exists; returns false if unable to create the
+// directory for any reason, including if the parent directory does not
+// exist. Not named "CreateDirectory" because that's a macro on Windows.
+bool FilePath::CreateFolder() const {
+#if GTEST_OS_WINDOWS_MOBILE
+ FilePath removed_sep(this->RemoveTrailingPathSeparator());
+ LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str());
+ int result = CreateDirectory(unicode, NULL) ? 0 : -1;
+ delete [] unicode;
+#elif GTEST_OS_WINDOWS
+ int result = _mkdir(pathname_.c_str());
+#else
+ int result = mkdir(pathname_.c_str(), 0777);
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+ if (result == -1) {
+ return this->DirectoryExists(); // An error is OK if the directory exists.
+ }
+ return true; // No error.
+}
+
+// If input name has a trailing separator character, remove it and return the
+// name, otherwise return the name string unmodified.
+// On Windows platform, uses \ as the separator, other platforms use /.
+FilePath FilePath::RemoveTrailingPathSeparator() const {
+ return IsDirectory()
+ ? FilePath(pathname_.substr(0, pathname_.length() - 1))
+ : *this;
+}
+
+// Removes any redundant separators that might be in the pathname.
+// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
+// redundancies that might be in a pathname involving "." or "..".
+// TODO(wan@google.com): handle Windows network shares (e.g. \\server\share).
+void FilePath::Normalize() {
+ if (pathname_.c_str() == NULL) {
+ pathname_ = "";
+ return;
+ }
+ const char* src = pathname_.c_str();
+ char* const dest = new char[pathname_.length() + 1];
+ char* dest_ptr = dest;
+ memset(dest_ptr, 0, pathname_.length() + 1);
+
+ while (*src != '\0') {
+ *dest_ptr = *src;
+ if (!IsPathSeparator(*src)) {
+ src++;
+ } else {
+#if GTEST_HAS_ALT_PATH_SEP_
+ if (*dest_ptr == kAlternatePathSeparator) {
+ *dest_ptr = kPathSeparator;
+ }
+#endif
+ while (IsPathSeparator(*src))
+ src++;
+ }
+ dest_ptr++;
+ }
+ *dest_ptr = '\0';
+ pathname_ = dest;
+ delete[] dest;
+}
+
+} // namespace internal
+} // namespace testing
diff --git a/extern/gtest/src/gtest-internal-inl.h b/extern/gtest/src/gtest-internal-inl.h
new file mode 100644
index 00000000000..35df303cca6
--- /dev/null
+++ b/extern/gtest/src/gtest-internal-inl.h
@@ -0,0 +1,1218 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Utility functions and classes used by the Google C++ testing framework.
+//
+// Author: wan@google.com (Zhanyong Wan)
+//
+// This file contains purely Google Test's internal implementation. Please
+// DO NOT #INCLUDE IT IN A USER PROGRAM.
+
+#ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_
+#define GTEST_SRC_GTEST_INTERNAL_INL_H_
+
+// GTEST_IMPLEMENTATION_ is defined to 1 iff the current translation unit is
+// part of Google Test's implementation; otherwise it's undefined.
+#if !GTEST_IMPLEMENTATION_
+// A user is trying to include this from his code - just say no.
+# error "gtest-internal-inl.h is part of Google Test's internal implementation."
+# error "It must not be included except by Google Test itself."
+#endif // GTEST_IMPLEMENTATION_
+
+#ifndef _WIN32_WCE
+# include <errno.h>
+#endif // !_WIN32_WCE
+#include <stddef.h>
+#include <stdlib.h> // For strtoll/_strtoul64/malloc/free.
+#include <string.h> // For memmove.
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "gtest/internal/gtest-port.h"
+
+#if GTEST_CAN_STREAM_RESULTS_
+# include <arpa/inet.h> // NOLINT
+# include <netdb.h> // NOLINT
+#endif
+
+#if GTEST_OS_WINDOWS
+# include <windows.h> // NOLINT
+#endif // GTEST_OS_WINDOWS
+
+#include "gtest/gtest.h" // NOLINT
+#include "gtest/gtest-spi.h"
+
+namespace testing {
+
+// Declares the flags.
+//
+// We don't want the users to modify this flag in the code, but want
+// Google Test's own unit tests to be able to access it. Therefore we
+// declare it here as opposed to in gtest.h.
+GTEST_DECLARE_bool_(death_test_use_fork);
+
+namespace internal {
+
+// The value of GetTestTypeId() as seen from within the Google Test
+// library. This is solely for testing GetTestTypeId().
+GTEST_API_ extern const TypeId kTestTypeIdInGoogleTest;
+
+// Names of the flags (needed for parsing Google Test flags).
+const char kAlsoRunDisabledTestsFlag[] = "also_run_disabled_tests";
+const char kBreakOnFailureFlag[] = "break_on_failure";
+const char kCatchExceptionsFlag[] = "catch_exceptions";
+const char kColorFlag[] = "color";
+const char kFilterFlag[] = "filter";
+const char kListTestsFlag[] = "list_tests";
+const char kOutputFlag[] = "output";
+const char kPrintTimeFlag[] = "print_time";
+const char kRandomSeedFlag[] = "random_seed";
+const char kRepeatFlag[] = "repeat";
+const char kShuffleFlag[] = "shuffle";
+const char kStackTraceDepthFlag[] = "stack_trace_depth";
+const char kStreamResultToFlag[] = "stream_result_to";
+const char kThrowOnFailureFlag[] = "throw_on_failure";
+
+// A valid random seed must be in [1, kMaxRandomSeed].
+const int kMaxRandomSeed = 99999;
+
+// g_help_flag is true iff the --help flag or an equivalent form is
+// specified on the command line.
+GTEST_API_ extern bool g_help_flag;
+
+// Returns the current time in milliseconds.
+GTEST_API_ TimeInMillis GetTimeInMillis();
+
+// Returns true iff Google Test should use colors in the output.
+GTEST_API_ bool ShouldUseColor(bool stdout_is_tty);
+
+// Formats the given time in milliseconds as seconds.
+GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms);
+
+// Converts the given time in milliseconds to a date string in the ISO 8601
+// format, without the timezone information. N.B.: due to the use the
+// non-reentrant localtime() function, this function is not thread safe. Do
+// not use it in any code that can be called from multiple threads.
+GTEST_API_ std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms);
+
+// Parses a string for an Int32 flag, in the form of "--flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+GTEST_API_ bool ParseInt32Flag(
+ const char* str, const char* flag, Int32* value);
+
+// Returns a random seed in range [1, kMaxRandomSeed] based on the
+// given --gtest_random_seed flag value.
+inline int GetRandomSeedFromFlag(Int32 random_seed_flag) {
+ const unsigned int raw_seed = (random_seed_flag == 0) ?
+ static_cast<unsigned int>(GetTimeInMillis()) :
+ static_cast<unsigned int>(random_seed_flag);
+
+ // Normalizes the actual seed to range [1, kMaxRandomSeed] such that
+ // it's easy to type.
+ const int normalized_seed =
+ static_cast<int>((raw_seed - 1U) %
+ static_cast<unsigned int>(kMaxRandomSeed)) + 1;
+ return normalized_seed;
+}
+
+// Returns the first valid random seed after 'seed'. The behavior is
+// undefined if 'seed' is invalid. The seed after kMaxRandomSeed is
+// considered to be 1.
+inline int GetNextRandomSeed(int seed) {
+ GTEST_CHECK_(1 <= seed && seed <= kMaxRandomSeed)
+ << "Invalid random seed " << seed << " - must be in [1, "
+ << kMaxRandomSeed << "].";
+ const int next_seed = seed + 1;
+ return (next_seed > kMaxRandomSeed) ? 1 : next_seed;
+}
+
+// This class saves the values of all Google Test flags in its c'tor, and
+// restores them in its d'tor.
+class GTestFlagSaver {
+ public:
+ // The c'tor.
+ GTestFlagSaver() {
+ also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests);
+ break_on_failure_ = GTEST_FLAG(break_on_failure);
+ catch_exceptions_ = GTEST_FLAG(catch_exceptions);
+ color_ = GTEST_FLAG(color);
+ death_test_style_ = GTEST_FLAG(death_test_style);
+ death_test_use_fork_ = GTEST_FLAG(death_test_use_fork);
+ filter_ = GTEST_FLAG(filter);
+ internal_run_death_test_ = GTEST_FLAG(internal_run_death_test);
+ list_tests_ = GTEST_FLAG(list_tests);
+ output_ = GTEST_FLAG(output);
+ print_time_ = GTEST_FLAG(print_time);
+ random_seed_ = GTEST_FLAG(random_seed);
+ repeat_ = GTEST_FLAG(repeat);
+ shuffle_ = GTEST_FLAG(shuffle);
+ stack_trace_depth_ = GTEST_FLAG(stack_trace_depth);
+ stream_result_to_ = GTEST_FLAG(stream_result_to);
+ throw_on_failure_ = GTEST_FLAG(throw_on_failure);
+ }
+
+ // The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS.
+ ~GTestFlagSaver() {
+ GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_;
+ GTEST_FLAG(break_on_failure) = break_on_failure_;
+ GTEST_FLAG(catch_exceptions) = catch_exceptions_;
+ GTEST_FLAG(color) = color_;
+ GTEST_FLAG(death_test_style) = death_test_style_;
+ GTEST_FLAG(death_test_use_fork) = death_test_use_fork_;
+ GTEST_FLAG(filter) = filter_;
+ GTEST_FLAG(internal_run_death_test) = internal_run_death_test_;
+ GTEST_FLAG(list_tests) = list_tests_;
+ GTEST_FLAG(output) = output_;
+ GTEST_FLAG(print_time) = print_time_;
+ GTEST_FLAG(random_seed) = random_seed_;
+ GTEST_FLAG(repeat) = repeat_;
+ GTEST_FLAG(shuffle) = shuffle_;
+ GTEST_FLAG(stack_trace_depth) = stack_trace_depth_;
+ GTEST_FLAG(stream_result_to) = stream_result_to_;
+ GTEST_FLAG(throw_on_failure) = throw_on_failure_;
+ }
+
+ private:
+ // Fields for saving the original values of flags.
+ bool also_run_disabled_tests_;
+ bool break_on_failure_;
+ bool catch_exceptions_;
+ std::string color_;
+ std::string death_test_style_;
+ bool death_test_use_fork_;
+ std::string filter_;
+ std::string internal_run_death_test_;
+ bool list_tests_;
+ std::string output_;
+ bool print_time_;
+ internal::Int32 random_seed_;
+ internal::Int32 repeat_;
+ bool shuffle_;
+ internal::Int32 stack_trace_depth_;
+ std::string stream_result_to_;
+ bool throw_on_failure_;
+} GTEST_ATTRIBUTE_UNUSED_;
+
+// Converts a Unicode code point to a narrow string in UTF-8 encoding.
+// code_point parameter is of type UInt32 because wchar_t may not be
+// wide enough to contain a code point.
+// If the code_point is not a valid Unicode code point
+// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted
+// to "(Invalid Unicode 0xXXXXXXXX)".
+GTEST_API_ std::string CodePointToUtf8(UInt32 code_point);
+
+// Converts a wide string to a narrow string in UTF-8 encoding.
+// The wide string is assumed to have the following encoding:
+// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS)
+// UTF-32 if sizeof(wchar_t) == 4 (on Linux)
+// Parameter str points to a null-terminated wide string.
+// Parameter num_chars may additionally limit the number
+// of wchar_t characters processed. -1 is used when the entire string
+// should be processed.
+// If the string contains code points that are not valid Unicode code points
+// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output
+// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding
+// and contains invalid UTF-16 surrogate pairs, values in those pairs
+// will be encoded as individual Unicode characters from Basic Normal Plane.
+GTEST_API_ std::string WideStringToUtf8(const wchar_t* str, int num_chars);
+
+// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file
+// if the variable is present. If a file already exists at this location, this
+// function will write over it. If the variable is present, but the file cannot
+// be created, prints an error and exits.
+void WriteToShardStatusFileIfNeeded();
+
+// Checks whether sharding is enabled by examining the relevant
+// environment variable values. If the variables are present,
+// but inconsistent (e.g., shard_index >= total_shards), prints
+// an error and exits. If in_subprocess_for_death_test, sharding is
+// disabled because it must only be applied to the original test
+// process. Otherwise, we could filter out death tests we intended to execute.
+GTEST_API_ bool ShouldShard(const char* total_shards_str,
+ const char* shard_index_str,
+ bool in_subprocess_for_death_test);
+
+// Parses the environment variable var as an Int32. If it is unset,
+// returns default_val. If it is not an Int32, prints an error and
+// and aborts.
+GTEST_API_ Int32 Int32FromEnvOrDie(const char* env_var, Int32 default_val);
+
+// Given the total number of shards, the shard index, and the test id,
+// returns true iff the test should be run on this shard. The test id is
+// some arbitrary but unique non-negative integer assigned to each test
+// method. Assumes that 0 <= shard_index < total_shards.
+GTEST_API_ bool ShouldRunTestOnShard(
+ int total_shards, int shard_index, int test_id);
+
+// STL container utilities.
+
+// Returns the number of elements in the given container that satisfy
+// the given predicate.
+template <class Container, typename Predicate>
+inline int CountIf(const Container& c, Predicate predicate) {
+ // Implemented as an explicit loop since std::count_if() in libCstd on
+ // Solaris has a non-standard signature.
+ int count = 0;
+ for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) {
+ if (predicate(*it))
+ ++count;
+ }
+ return count;
+}
+
+// Applies a function/functor to each element in the container.
+template <class Container, typename Functor>
+void ForEach(const Container& c, Functor functor) {
+ std::for_each(c.begin(), c.end(), functor);
+}
+
+// Returns the i-th element of the vector, or default_value if i is not
+// in range [0, v.size()).
+template <typename E>
+inline E GetElementOr(const std::vector<E>& v, int i, E default_value) {
+ return (i < 0 || i >= static_cast<int>(v.size())) ? default_value : v[i];
+}
+
+// Performs an in-place shuffle of a range of the vector's elements.
+// 'begin' and 'end' are element indices as an STL-style range;
+// i.e. [begin, end) are shuffled, where 'end' == size() means to
+// shuffle to the end of the vector.
+template <typename E>
+void ShuffleRange(internal::Random* random, int begin, int end,
+ std::vector<E>* v) {
+ const int size = static_cast<int>(v->size());
+ GTEST_CHECK_(0 <= begin && begin <= size)
+ << "Invalid shuffle range start " << begin << ": must be in range [0, "
+ << size << "].";
+ GTEST_CHECK_(begin <= end && end <= size)
+ << "Invalid shuffle range finish " << end << ": must be in range ["
+ << begin << ", " << size << "].";
+
+ // Fisher-Yates shuffle, from
+ // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
+ for (int range_width = end - begin; range_width >= 2; range_width--) {
+ const int last_in_range = begin + range_width - 1;
+ const int selected = begin + random->Generate(range_width);
+ std::swap((*v)[selected], (*v)[last_in_range]);
+ }
+}
+
+// Performs an in-place shuffle of the vector's elements.
+template <typename E>
+inline void Shuffle(internal::Random* random, std::vector<E>* v) {
+ ShuffleRange(random, 0, static_cast<int>(v->size()), v);
+}
+
+// A function for deleting an object. Handy for being used as a
+// functor.
+template <typename T>
+static void Delete(T* x) {
+ delete x;
+}
+
+// A predicate that checks the key of a TestProperty against a known key.
+//
+// TestPropertyKeyIs is copyable.
+class TestPropertyKeyIs {
+ public:
+ // Constructor.
+ //
+ // TestPropertyKeyIs has NO default constructor.
+ explicit TestPropertyKeyIs(const std::string& key) : key_(key) {}
+
+ // Returns true iff the test name of test property matches on key_.
+ bool operator()(const TestProperty& test_property) const {
+ return test_property.key() == key_;
+ }
+
+ private:
+ std::string key_;
+};
+
+// Class UnitTestOptions.
+//
+// This class contains functions for processing options the user
+// specifies when running the tests. It has only static members.
+//
+// In most cases, the user can specify an option using either an
+// environment variable or a command line flag. E.g. you can set the
+// test filter using either GTEST_FILTER or --gtest_filter. If both
+// the variable and the flag are present, the latter overrides the
+// former.
+class GTEST_API_ UnitTestOptions {
+ public:
+ // Functions for processing the gtest_output flag.
+
+ // Returns the output format, or "" for normal printed output.
+ static std::string GetOutputFormat();
+
+ // Returns the absolute path of the requested output file, or the
+ // default (test_detail.xml in the original working directory) if
+ // none was explicitly specified.
+ static std::string GetAbsolutePathToOutputFile();
+
+ // Functions for processing the gtest_filter flag.
+
+ // Returns true iff the wildcard pattern matches the string. The
+ // first ':' or '\0' character in pattern marks the end of it.
+ //
+ // This recursive algorithm isn't very efficient, but is clear and
+ // works well enough for matching test names, which are short.
+ static bool PatternMatchesString(const char *pattern, const char *str);
+
+ // Returns true iff the user-specified filter matches the test case
+ // name and the test name.
+ static bool FilterMatchesTest(const std::string &test_case_name,
+ const std::string &test_name);
+
+#if GTEST_OS_WINDOWS
+ // Function for supporting the gtest_catch_exception flag.
+
+ // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the
+ // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise.
+ // This function is useful as an __except condition.
+ static int GTestShouldProcessSEH(DWORD exception_code);
+#endif // GTEST_OS_WINDOWS
+
+ // Returns true if "name" matches the ':' separated list of glob-style
+ // filters in "filter".
+ static bool MatchesFilter(const std::string& name, const char* filter);
+};
+
+// Returns the current application's name, removing directory path if that
+// is present. Used by UnitTestOptions::GetOutputFile.
+GTEST_API_ FilePath GetCurrentExecutableName();
+
+// The role interface for getting the OS stack trace as a string.
+class OsStackTraceGetterInterface {
+ public:
+ OsStackTraceGetterInterface() {}
+ virtual ~OsStackTraceGetterInterface() {}
+
+ // Returns the current OS stack trace as an std::string. Parameters:
+ //
+ // max_depth - the maximum number of stack frames to be included
+ // in the trace.
+ // skip_count - the number of top frames to be skipped; doesn't count
+ // against max_depth.
+ virtual string CurrentStackTrace(int max_depth, int skip_count) = 0;
+
+ // UponLeavingGTest() should be called immediately before Google Test calls
+ // user code. It saves some information about the current stack that
+ // CurrentStackTrace() will use to find and hide Google Test stack frames.
+ virtual void UponLeavingGTest() = 0;
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetterInterface);
+};
+
+// A working implementation of the OsStackTraceGetterInterface interface.
+class OsStackTraceGetter : public OsStackTraceGetterInterface {
+ public:
+ OsStackTraceGetter() : caller_frame_(NULL) {}
+
+ virtual string CurrentStackTrace(int max_depth, int skip_count)
+ GTEST_LOCK_EXCLUDED_(mutex_);
+
+ virtual void UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_);
+
+ // This string is inserted in place of stack frames that are part of
+ // Google Test's implementation.
+ static const char* const kElidedFramesMarker;
+
+ private:
+ Mutex mutex_; // protects all internal state
+
+ // We save the stack frame below the frame that calls user code.
+ // We do this because the address of the frame immediately below
+ // the user code changes between the call to UponLeavingGTest()
+ // and any calls to CurrentStackTrace() from within the user code.
+ void* caller_frame_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter);
+};
+
+// Information about a Google Test trace point.
+struct TraceInfo {
+ const char* file;
+ int line;
+ std::string message;
+};
+
+// This is the default global test part result reporter used in UnitTestImpl.
+// This class should only be used by UnitTestImpl.
+class DefaultGlobalTestPartResultReporter
+ : public TestPartResultReporterInterface {
+ public:
+ explicit DefaultGlobalTestPartResultReporter(UnitTestImpl* unit_test);
+ // Implements the TestPartResultReporterInterface. Reports the test part
+ // result in the current test.
+ virtual void ReportTestPartResult(const TestPartResult& result);
+
+ private:
+ UnitTestImpl* const unit_test_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultGlobalTestPartResultReporter);
+};
+
+// This is the default per thread test part result reporter used in
+// UnitTestImpl. This class should only be used by UnitTestImpl.
+class DefaultPerThreadTestPartResultReporter
+ : public TestPartResultReporterInterface {
+ public:
+ explicit DefaultPerThreadTestPartResultReporter(UnitTestImpl* unit_test);
+ // Implements the TestPartResultReporterInterface. The implementation just
+ // delegates to the current global test part result reporter of *unit_test_.
+ virtual void ReportTestPartResult(const TestPartResult& result);
+
+ private:
+ UnitTestImpl* const unit_test_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultPerThreadTestPartResultReporter);
+};
+
+// The private implementation of the UnitTest class. We don't protect
+// the methods under a mutex, as this class is not accessible by a
+// user and the UnitTest class that delegates work to this class does
+// proper locking.
+class GTEST_API_ UnitTestImpl {
+ public:
+ explicit UnitTestImpl(UnitTest* parent);
+ virtual ~UnitTestImpl();
+
+ // There are two different ways to register your own TestPartResultReporter.
+ // You can register your own repoter to listen either only for test results
+ // from the current thread or for results from all threads.
+ // By default, each per-thread test result repoter just passes a new
+ // TestPartResult to the global test result reporter, which registers the
+ // test part result for the currently running test.
+
+ // Returns the global test part result reporter.
+ TestPartResultReporterInterface* GetGlobalTestPartResultReporter();
+
+ // Sets the global test part result reporter.
+ void SetGlobalTestPartResultReporter(
+ TestPartResultReporterInterface* reporter);
+
+ // Returns the test part result reporter for the current thread.
+ TestPartResultReporterInterface* GetTestPartResultReporterForCurrentThread();
+
+ // Sets the test part result reporter for the current thread.
+ void SetTestPartResultReporterForCurrentThread(
+ TestPartResultReporterInterface* reporter);
+
+ // Gets the number of successful test cases.
+ int successful_test_case_count() const;
+
+ // Gets the number of failed test cases.
+ int failed_test_case_count() const;
+
+ // Gets the number of all test cases.
+ int total_test_case_count() const;
+
+ // Gets the number of all test cases that contain at least one test
+ // that should run.
+ int test_case_to_run_count() const;
+
+ // Gets the number of successful tests.
+ int successful_test_count() const;
+
+ // Gets the number of failed tests.
+ int failed_test_count() const;
+
+ // Gets the number of disabled tests that will be reported in the XML report.
+ int reportable_disabled_test_count() const;
+
+ // Gets the number of disabled tests.
+ int disabled_test_count() const;
+
+ // Gets the number of tests to be printed in the XML report.
+ int reportable_test_count() const;
+
+ // Gets the number of all tests.
+ int total_test_count() const;
+
+ // Gets the number of tests that should run.
+ int test_to_run_count() const;
+
+ // Gets the time of the test program start, in ms from the start of the
+ // UNIX epoch.
+ TimeInMillis start_timestamp() const { return start_timestamp_; }
+
+ // Gets the elapsed time, in milliseconds.
+ TimeInMillis elapsed_time() const { return elapsed_time_; }
+
+ // Returns true iff the unit test passed (i.e. all test cases passed).
+ bool Passed() const { return !Failed(); }
+
+ // Returns true iff the unit test failed (i.e. some test case failed
+ // or something outside of all tests failed).
+ bool Failed() const {
+ return failed_test_case_count() > 0 || ad_hoc_test_result()->Failed();
+ }
+
+ // Gets the i-th test case among all the test cases. i can range from 0 to
+ // total_test_case_count() - 1. If i is not in that range, returns NULL.
+ const TestCase* GetTestCase(int i) const {
+ const int index = GetElementOr(test_case_indices_, i, -1);
+ return index < 0 ? NULL : test_cases_[i];
+ }
+
+ // Gets the i-th test case among all the test cases. i can range from 0 to
+ // total_test_case_count() - 1. If i is not in that range, returns NULL.
+ TestCase* GetMutableTestCase(int i) {
+ const int index = GetElementOr(test_case_indices_, i, -1);
+ return index < 0 ? NULL : test_cases_[index];
+ }
+
+ // Provides access to the event listener list.
+ TestEventListeners* listeners() { return &listeners_; }
+
+ // Returns the TestResult for the test that's currently running, or
+ // the TestResult for the ad hoc test if no test is running.
+ TestResult* current_test_result();
+
+ // Returns the TestResult for the ad hoc test.
+ const TestResult* ad_hoc_test_result() const { return &ad_hoc_test_result_; }
+
+ // Sets the OS stack trace getter.
+ //
+ // Does nothing if the input and the current OS stack trace getter
+ // are the same; otherwise, deletes the old getter and makes the
+ // input the current getter.
+ void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter);
+
+ // Returns the current OS stack trace getter if it is not NULL;
+ // otherwise, creates an OsStackTraceGetter, makes it the current
+ // getter, and returns it.
+ OsStackTraceGetterInterface* os_stack_trace_getter();
+
+ // Returns the current OS stack trace as an std::string.
+ //
+ // The maximum number of stack frames to be included is specified by
+ // the gtest_stack_trace_depth flag. The skip_count parameter
+ // specifies the number of top frames to be skipped, which doesn't
+ // count against the number of frames to be included.
+ //
+ // For example, if Foo() calls Bar(), which in turn calls
+ // CurrentOsStackTraceExceptTop(1), Foo() will be included in the
+ // trace but Bar() and CurrentOsStackTraceExceptTop() won't.
+ std::string CurrentOsStackTraceExceptTop(int skip_count) GTEST_NO_INLINE_;
+
+ // Finds and returns a TestCase with the given name. If one doesn't
+ // exist, creates one and returns it.
+ //
+ // Arguments:
+ //
+ // test_case_name: name of the test case
+ // type_param: the name of the test's type parameter, or NULL if
+ // this is not a typed or a type-parameterized test.
+ // set_up_tc: pointer to the function that sets up the test case
+ // tear_down_tc: pointer to the function that tears down the test case
+ TestCase* GetTestCase(const char* test_case_name,
+ const char* type_param,
+ Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc);
+
+ // Adds a TestInfo to the unit test.
+ //
+ // Arguments:
+ //
+ // set_up_tc: pointer to the function that sets up the test case
+ // tear_down_tc: pointer to the function that tears down the test case
+ // test_info: the TestInfo object
+ void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc,
+ TestInfo* test_info) {
+ // In order to support thread-safe death tests, we need to
+ // remember the original working directory when the test program
+ // was first invoked. We cannot do this in RUN_ALL_TESTS(), as
+ // the user may have changed the current directory before calling
+ // RUN_ALL_TESTS(). Therefore we capture the current directory in
+ // AddTestInfo(), which is called to register a TEST or TEST_F
+ // before main() is reached.
+ if (original_working_dir_.IsEmpty()) {
+ original_working_dir_.Set(FilePath::GetCurrentDir());
+ GTEST_CHECK_(!original_working_dir_.IsEmpty())
+ << "Failed to get the current working directory.";
+ }
+
+ GetTestCase(test_info->test_case_name(),
+ test_info->type_param(),
+ set_up_tc,
+ tear_down_tc)->AddTestInfo(test_info);
+ }
+
+#if GTEST_HAS_PARAM_TEST
+ // Returns ParameterizedTestCaseRegistry object used to keep track of
+ // value-parameterized tests and instantiate and register them.
+ internal::ParameterizedTestCaseRegistry& parameterized_test_registry() {
+ return parameterized_test_registry_;
+ }
+#endif // GTEST_HAS_PARAM_TEST
+
+ // Sets the TestCase object for the test that's currently running.
+ void set_current_test_case(TestCase* a_current_test_case) {
+ current_test_case_ = a_current_test_case;
+ }
+
+ // Sets the TestInfo object for the test that's currently running. If
+ // current_test_info is NULL, the assertion results will be stored in
+ // ad_hoc_test_result_.
+ void set_current_test_info(TestInfo* a_current_test_info) {
+ current_test_info_ = a_current_test_info;
+ }
+
+ // Registers all parameterized tests defined using TEST_P and
+ // INSTANTIATE_TEST_CASE_P, creating regular tests for each test/parameter
+ // combination. This method can be called more then once; it has guards
+ // protecting from registering the tests more then once. If
+ // value-parameterized tests are disabled, RegisterParameterizedTests is
+ // present but does nothing.
+ void RegisterParameterizedTests();
+
+ // Runs all tests in this UnitTest object, prints the result, and
+ // returns true if all tests are successful. If any exception is
+ // thrown during a test, this test is considered to be failed, but
+ // the rest of the tests will still be run.
+ bool RunAllTests();
+
+ // Clears the results of all tests, except the ad hoc tests.
+ void ClearNonAdHocTestResult() {
+ ForEach(test_cases_, TestCase::ClearTestCaseResult);
+ }
+
+ // Clears the results of ad-hoc test assertions.
+ void ClearAdHocTestResult() {
+ ad_hoc_test_result_.Clear();
+ }
+
+ // Adds a TestProperty to the current TestResult object when invoked in a
+ // context of a test or a test case, or to the global property set. If the
+ // result already contains a property with the same key, the value will be
+ // updated.
+ void RecordProperty(const TestProperty& test_property);
+
+ enum ReactionToSharding {
+ HONOR_SHARDING_PROTOCOL,
+ IGNORE_SHARDING_PROTOCOL
+ };
+
+ // Matches the full name of each test against the user-specified
+ // filter to decide whether the test should run, then records the
+ // result in each TestCase and TestInfo object.
+ // If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests
+ // based on sharding variables in the environment.
+ // Returns the number of tests that should run.
+ int FilterTests(ReactionToSharding shard_tests);
+
+ // Prints the names of the tests matching the user-specified filter flag.
+ void ListTestsMatchingFilter();
+
+ const TestCase* current_test_case() const { return current_test_case_; }
+ TestInfo* current_test_info() { return current_test_info_; }
+ const TestInfo* current_test_info() const { return current_test_info_; }
+
+ // Returns the vector of environments that need to be set-up/torn-down
+ // before/after the tests are run.
+ std::vector<Environment*>& environments() { return environments_; }
+
+ // Getters for the per-thread Google Test trace stack.
+ std::vector<TraceInfo>& gtest_trace_stack() {
+ return *(gtest_trace_stack_.pointer());
+ }
+ const std::vector<TraceInfo>& gtest_trace_stack() const {
+ return gtest_trace_stack_.get();
+ }
+
+#if GTEST_HAS_DEATH_TEST
+ void InitDeathTestSubprocessControlInfo() {
+ internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag());
+ }
+ // Returns a pointer to the parsed --gtest_internal_run_death_test
+ // flag, or NULL if that flag was not specified.
+ // This information is useful only in a death test child process.
+ // Must not be called before a call to InitGoogleTest.
+ const InternalRunDeathTestFlag* internal_run_death_test_flag() const {
+ return internal_run_death_test_flag_.get();
+ }
+
+ // Returns a pointer to the current death test factory.
+ internal::DeathTestFactory* death_test_factory() {
+ return death_test_factory_.get();
+ }
+
+ void SuppressTestEventsIfInSubprocess();
+
+ friend class ReplaceDeathTestFactory;
+#endif // GTEST_HAS_DEATH_TEST
+
+ // Initializes the event listener performing XML output as specified by
+ // UnitTestOptions. Must not be called before InitGoogleTest.
+ void ConfigureXmlOutput();
+
+#if GTEST_CAN_STREAM_RESULTS_
+ // Initializes the event listener for streaming test results to a socket.
+ // Must not be called before InitGoogleTest.
+ void ConfigureStreamingOutput();
+#endif
+
+ // Performs initialization dependent upon flag values obtained in
+ // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to
+ // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest
+ // this function is also called from RunAllTests. Since this function can be
+ // called more than once, it has to be idempotent.
+ void PostFlagParsingInit();
+
+ // Gets the random seed used at the start of the current test iteration.
+ int random_seed() const { return random_seed_; }
+
+ // Gets the random number generator.
+ internal::Random* random() { return &random_; }
+
+ // Shuffles all test cases, and the tests within each test case,
+ // making sure that death tests are still run first.
+ void ShuffleTests();
+
+ // Restores the test cases and tests to their order before the first shuffle.
+ void UnshuffleTests();
+
+ // Returns the value of GTEST_FLAG(catch_exceptions) at the moment
+ // UnitTest::Run() starts.
+ bool catch_exceptions() const { return catch_exceptions_; }
+
+ private:
+ friend class ::testing::UnitTest;
+
+ // Used by UnitTest::Run() to capture the state of
+ // GTEST_FLAG(catch_exceptions) at the moment it starts.
+ void set_catch_exceptions(bool value) { catch_exceptions_ = value; }
+
+ // The UnitTest object that owns this implementation object.
+ UnitTest* const parent_;
+
+ // The working directory when the first TEST() or TEST_F() was
+ // executed.
+ internal::FilePath original_working_dir_;
+
+ // The default test part result reporters.
+ DefaultGlobalTestPartResultReporter default_global_test_part_result_reporter_;
+ DefaultPerThreadTestPartResultReporter
+ default_per_thread_test_part_result_reporter_;
+
+ // Points to (but doesn't own) the global test part result reporter.
+ TestPartResultReporterInterface* global_test_part_result_repoter_;
+
+ // Protects read and write access to global_test_part_result_reporter_.
+ internal::Mutex global_test_part_result_reporter_mutex_;
+
+ // Points to (but doesn't own) the per-thread test part result reporter.
+ internal::ThreadLocal<TestPartResultReporterInterface*>
+ per_thread_test_part_result_reporter_;
+
+ // The vector of environments that need to be set-up/torn-down
+ // before/after the tests are run.
+ std::vector<Environment*> environments_;
+
+ // The vector of TestCases in their original order. It owns the
+ // elements in the vector.
+ std::vector<TestCase*> test_cases_;
+
+ // Provides a level of indirection for the test case list to allow
+ // easy shuffling and restoring the test case order. The i-th
+ // element of this vector is the index of the i-th test case in the
+ // shuffled order.
+ std::vector<int> test_case_indices_;
+
+#if GTEST_HAS_PARAM_TEST
+ // ParameterizedTestRegistry object used to register value-parameterized
+ // tests.
+ internal::ParameterizedTestCaseRegistry parameterized_test_registry_;
+
+ // Indicates whether RegisterParameterizedTests() has been called already.
+ bool parameterized_tests_registered_;
+#endif // GTEST_HAS_PARAM_TEST
+
+ // Index of the last death test case registered. Initially -1.
+ int last_death_test_case_;
+
+ // This points to the TestCase for the currently running test. It
+ // changes as Google Test goes through one test case after another.
+ // When no test is running, this is set to NULL and Google Test
+ // stores assertion results in ad_hoc_test_result_. Initially NULL.
+ TestCase* current_test_case_;
+
+ // This points to the TestInfo for the currently running test. It
+ // changes as Google Test goes through one test after another. When
+ // no test is running, this is set to NULL and Google Test stores
+ // assertion results in ad_hoc_test_result_. Initially NULL.
+ TestInfo* current_test_info_;
+
+ // Normally, a user only writes assertions inside a TEST or TEST_F,
+ // or inside a function called by a TEST or TEST_F. Since Google
+ // Test keeps track of which test is current running, it can
+ // associate such an assertion with the test it belongs to.
+ //
+ // If an assertion is encountered when no TEST or TEST_F is running,
+ // Google Test attributes the assertion result to an imaginary "ad hoc"
+ // test, and records the result in ad_hoc_test_result_.
+ TestResult ad_hoc_test_result_;
+
+ // The list of event listeners that can be used to track events inside
+ // Google Test.
+ TestEventListeners listeners_;
+
+ // The OS stack trace getter. Will be deleted when the UnitTest
+ // object is destructed. By default, an OsStackTraceGetter is used,
+ // but the user can set this field to use a custom getter if that is
+ // desired.
+ OsStackTraceGetterInterface* os_stack_trace_getter_;
+
+ // True iff PostFlagParsingInit() has been called.
+ bool post_flag_parse_init_performed_;
+
+ // The random number seed used at the beginning of the test run.
+ int random_seed_;
+
+ // Our random number generator.
+ internal::Random random_;
+
+ // The time of the test program start, in ms from the start of the
+ // UNIX epoch.
+ TimeInMillis start_timestamp_;
+
+ // How long the test took to run, in milliseconds.
+ TimeInMillis elapsed_time_;
+
+#if GTEST_HAS_DEATH_TEST
+ // The decomposed components of the gtest_internal_run_death_test flag,
+ // parsed when RUN_ALL_TESTS is called.
+ internal::scoped_ptr<InternalRunDeathTestFlag> internal_run_death_test_flag_;
+ internal::scoped_ptr<internal::DeathTestFactory> death_test_factory_;
+#endif // GTEST_HAS_DEATH_TEST
+
+ // A per-thread stack of traces created by the SCOPED_TRACE() macro.
+ internal::ThreadLocal<std::vector<TraceInfo> > gtest_trace_stack_;
+
+ // The value of GTEST_FLAG(catch_exceptions) at the moment RunAllTests()
+ // starts.
+ bool catch_exceptions_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTestImpl);
+}; // class UnitTestImpl
+
+// Convenience function for accessing the global UnitTest
+// implementation object.
+inline UnitTestImpl* GetUnitTestImpl() {
+ return UnitTest::GetInstance()->impl();
+}
+
+#if GTEST_USES_SIMPLE_RE
+
+// Internal helper functions for implementing the simple regular
+// expression matcher.
+GTEST_API_ bool IsInSet(char ch, const char* str);
+GTEST_API_ bool IsAsciiDigit(char ch);
+GTEST_API_ bool IsAsciiPunct(char ch);
+GTEST_API_ bool IsRepeat(char ch);
+GTEST_API_ bool IsAsciiWhiteSpace(char ch);
+GTEST_API_ bool IsAsciiWordChar(char ch);
+GTEST_API_ bool IsValidEscape(char ch);
+GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch);
+GTEST_API_ bool ValidateRegex(const char* regex);
+GTEST_API_ bool MatchRegexAtHead(const char* regex, const char* str);
+GTEST_API_ bool MatchRepetitionAndRegexAtHead(
+ bool escaped, char ch, char repeat, const char* regex, const char* str);
+GTEST_API_ bool MatchRegexAnywhere(const char* regex, const char* str);
+
+#endif // GTEST_USES_SIMPLE_RE
+
+// Parses the command line for Google Test flags, without initializing
+// other parts of Google Test.
+GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv);
+GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv);
+
+#if GTEST_HAS_DEATH_TEST
+
+// Returns the message describing the last system error, regardless of the
+// platform.
+GTEST_API_ std::string GetLastErrnoDescription();
+
+# if GTEST_OS_WINDOWS
+// Provides leak-safe Windows kernel handle ownership.
+class AutoHandle {
+ public:
+ AutoHandle() : handle_(INVALID_HANDLE_VALUE) {}
+ explicit AutoHandle(HANDLE handle) : handle_(handle) {}
+
+ ~AutoHandle() { Reset(); }
+
+ HANDLE Get() const { return handle_; }
+ void Reset() { Reset(INVALID_HANDLE_VALUE); }
+ void Reset(HANDLE handle) {
+ if (handle != handle_) {
+ if (handle_ != INVALID_HANDLE_VALUE)
+ ::CloseHandle(handle_);
+ handle_ = handle;
+ }
+ }
+
+ private:
+ HANDLE handle_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle);
+};
+# endif // GTEST_OS_WINDOWS
+
+// Attempts to parse a string into a positive integer pointed to by the
+// number parameter. Returns true if that is possible.
+// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use
+// it here.
+template <typename Integer>
+bool ParseNaturalNumber(const ::std::string& str, Integer* number) {
+ // Fail fast if the given string does not begin with a digit;
+ // this bypasses strtoXXX's "optional leading whitespace and plus
+ // or minus sign" semantics, which are undesirable here.
+ if (str.empty() || !IsDigit(str[0])) {
+ return false;
+ }
+ errno = 0;
+
+ char* end;
+ // BiggestConvertible is the largest integer type that system-provided
+ // string-to-number conversion routines can return.
+
+# if GTEST_OS_WINDOWS && !defined(__GNUC__)
+
+ // MSVC and C++ Builder define __int64 instead of the standard long long.
+ typedef unsigned __int64 BiggestConvertible;
+ const BiggestConvertible parsed = _strtoui64(str.c_str(), &end, 10);
+
+# else
+
+ typedef unsigned long long BiggestConvertible; // NOLINT
+ const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10);
+
+# endif // GTEST_OS_WINDOWS && !defined(__GNUC__)
+
+ const bool parse_success = *end == '\0' && errno == 0;
+
+ // TODO(vladl@google.com): Convert this to compile time assertion when it is
+ // available.
+ GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed));
+
+ const Integer result = static_cast<Integer>(parsed);
+ if (parse_success && static_cast<BiggestConvertible>(result) == parsed) {
+ *number = result;
+ return true;
+ }
+ return false;
+}
+#endif // GTEST_HAS_DEATH_TEST
+
+// TestResult contains some private methods that should be hidden from
+// Google Test user but are required for testing. This class allow our tests
+// to access them.
+//
+// This class is supplied only for the purpose of testing Google Test's own
+// constructs. Do not use it in user tests, either directly or indirectly.
+class TestResultAccessor {
+ public:
+ static void RecordProperty(TestResult* test_result,
+ const std::string& xml_element,
+ const TestProperty& property) {
+ test_result->RecordProperty(xml_element, property);
+ }
+
+ static void ClearTestPartResults(TestResult* test_result) {
+ test_result->ClearTestPartResults();
+ }
+
+ static const std::vector<testing::TestPartResult>& test_part_results(
+ const TestResult& test_result) {
+ return test_result.test_part_results();
+ }
+};
+
+#if GTEST_CAN_STREAM_RESULTS_
+
+// Streams test results to the given port on the given host machine.
+class StreamingListener : public EmptyTestEventListener {
+ public:
+ // Abstract base class for writing strings to a socket.
+ class AbstractSocketWriter {
+ public:
+ virtual ~AbstractSocketWriter() {}
+
+ // Sends a string to the socket.
+ virtual void Send(const string& message) = 0;
+
+ // Closes the socket.
+ virtual void CloseConnection() {}
+
+ // Sends a string and a newline to the socket.
+ void SendLn(const string& message) {
+ Send(message + "\n");
+ }
+ };
+
+ // Concrete class for actually writing strings to a socket.
+ class SocketWriter : public AbstractSocketWriter {
+ public:
+ SocketWriter(const string& host, const string& port)
+ : sockfd_(-1), host_name_(host), port_num_(port) {
+ MakeConnection();
+ }
+
+ virtual ~SocketWriter() {
+ if (sockfd_ != -1)
+ CloseConnection();
+ }
+
+ // Sends a string to the socket.
+ virtual void Send(const string& message) {
+ GTEST_CHECK_(sockfd_ != -1)
+ << "Send() can be called only when there is a connection.";
+
+ const int len = static_cast<int>(message.length());
+ if (write(sockfd_, message.c_str(), len) != len) {
+ GTEST_LOG_(WARNING)
+ << "stream_result_to: failed to stream to "
+ << host_name_ << ":" << port_num_;
+ }
+ }
+
+ private:
+ // Creates a client socket and connects to the server.
+ void MakeConnection();
+
+ // Closes the socket.
+ void CloseConnection() {
+ GTEST_CHECK_(sockfd_ != -1)
+ << "CloseConnection() can be called only when there is a connection.";
+
+ close(sockfd_);
+ sockfd_ = -1;
+ }
+
+ int sockfd_; // socket file descriptor
+ const string host_name_;
+ const string port_num_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter);
+ }; // class SocketWriter
+
+ // Escapes '=', '&', '%', and '\n' characters in str as "%xx".
+ static string UrlEncode(const char* str);
+
+ StreamingListener(const string& host, const string& port)
+ : socket_writer_(new SocketWriter(host, port)) { Start(); }
+
+ explicit StreamingListener(AbstractSocketWriter* socket_writer)
+ : socket_writer_(socket_writer) { Start(); }
+
+ void OnTestProgramStart(const UnitTest& /* unit_test */) {
+ SendLn("event=TestProgramStart");
+ }
+
+ void OnTestProgramEnd(const UnitTest& unit_test) {
+ // Note that Google Test current only report elapsed time for each
+ // test iteration, not for the entire test program.
+ SendLn("event=TestProgramEnd&passed=" + FormatBool(unit_test.Passed()));
+
+ // Notify the streaming server to stop.
+ socket_writer_->CloseConnection();
+ }
+
+ void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) {
+ SendLn("event=TestIterationStart&iteration=" +
+ StreamableToString(iteration));
+ }
+
+ void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) {
+ SendLn("event=TestIterationEnd&passed=" +
+ FormatBool(unit_test.Passed()) + "&elapsed_time=" +
+ StreamableToString(unit_test.elapsed_time()) + "ms");
+ }
+
+ void OnTestCaseStart(const TestCase& test_case) {
+ SendLn(std::string("event=TestCaseStart&name=") + test_case.name());
+ }
+
+ void OnTestCaseEnd(const TestCase& test_case) {
+ SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed())
+ + "&elapsed_time=" + StreamableToString(test_case.elapsed_time())
+ + "ms");
+ }
+
+ void OnTestStart(const TestInfo& test_info) {
+ SendLn(std::string("event=TestStart&name=") + test_info.name());
+ }
+
+ void OnTestEnd(const TestInfo& test_info) {
+ SendLn("event=TestEnd&passed=" +
+ FormatBool((test_info.result())->Passed()) +
+ "&elapsed_time=" +
+ StreamableToString((test_info.result())->elapsed_time()) + "ms");
+ }
+
+ void OnTestPartResult(const TestPartResult& test_part_result) {
+ const char* file_name = test_part_result.file_name();
+ if (file_name == NULL)
+ file_name = "";
+ SendLn("event=TestPartResult&file=" + UrlEncode(file_name) +
+ "&line=" + StreamableToString(test_part_result.line_number()) +
+ "&message=" + UrlEncode(test_part_result.message()));
+ }
+
+ private:
+ // Sends the given message and a newline to the socket.
+ void SendLn(const string& message) { socket_writer_->SendLn(message); }
+
+ // Called at the start of streaming to notify the receiver what
+ // protocol we are using.
+ void Start() { SendLn("gtest_streaming_protocol_version=1.0"); }
+
+ string FormatBool(bool value) { return value ? "1" : "0"; }
+
+ const scoped_ptr<AbstractSocketWriter> socket_writer_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener);
+}; // class StreamingListener
+
+#endif // GTEST_CAN_STREAM_RESULTS_
+
+} // namespace internal
+} // namespace testing
+
+#endif // GTEST_SRC_GTEST_INTERNAL_INL_H_
diff --git a/extern/gtest/src/gtest-port.cc b/extern/gtest/src/gtest-port.cc
new file mode 100644
index 00000000000..0c4df5f29a7
--- /dev/null
+++ b/extern/gtest/src/gtest-port.cc
@@ -0,0 +1,805 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+#include "gtest/internal/gtest-port.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#if GTEST_OS_WINDOWS_MOBILE
+# include <windows.h> // For TerminateProcess()
+#elif GTEST_OS_WINDOWS
+# include <io.h>
+# include <sys/stat.h>
+#else
+# include <unistd.h>
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+#if GTEST_OS_MAC
+# include <mach/mach_init.h>
+# include <mach/task.h>
+# include <mach/vm_map.h>
+#endif // GTEST_OS_MAC
+
+#if GTEST_OS_QNX
+# include <devctl.h>
+# include <sys/procfs.h>
+#endif // GTEST_OS_QNX
+
+#include "gtest/gtest-spi.h"
+#include "gtest/gtest-message.h"
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-string.h"
+
+// Indicates that this translation unit is part of Google Test's
+// implementation. It must come before gtest-internal-inl.h is
+// included, or there will be a compiler error. This trick is to
+// prevent a user from accidentally including gtest-internal-inl.h in
+// his code.
+#define GTEST_IMPLEMENTATION_ 1
+#include "src/gtest-internal-inl.h"
+#undef GTEST_IMPLEMENTATION_
+
+namespace testing {
+namespace internal {
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+// MSVC and C++Builder do not provide a definition of STDERR_FILENO.
+const int kStdOutFileno = 1;
+const int kStdErrFileno = 2;
+#else
+const int kStdOutFileno = STDOUT_FILENO;
+const int kStdErrFileno = STDERR_FILENO;
+#endif // _MSC_VER
+
+#if GTEST_OS_MAC
+
+// Returns the number of threads running in the process, or 0 to indicate that
+// we cannot detect it.
+size_t GetThreadCount() {
+ const task_t task = mach_task_self();
+ mach_msg_type_number_t thread_count;
+ thread_act_array_t thread_list;
+ const kern_return_t status = task_threads(task, &thread_list, &thread_count);
+ if (status == KERN_SUCCESS) {
+ // task_threads allocates resources in thread_list and we need to free them
+ // to avoid leaks.
+ vm_deallocate(task,
+ reinterpret_cast<vm_address_t>(thread_list),
+ sizeof(thread_t) * thread_count);
+ return static_cast<size_t>(thread_count);
+ } else {
+ return 0;
+ }
+}
+
+#elif GTEST_OS_QNX
+
+// Returns the number of threads running in the process, or 0 to indicate that
+// we cannot detect it.
+size_t GetThreadCount() {
+ const int fd = open("/proc/self/as", O_RDONLY);
+ if (fd < 0) {
+ return 0;
+ }
+ procfs_info process_info;
+ const int status =
+ devctl(fd, DCMD_PROC_INFO, &process_info, sizeof(process_info), NULL);
+ close(fd);
+ if (status == EOK) {
+ return static_cast<size_t>(process_info.num_threads);
+ } else {
+ return 0;
+ }
+}
+
+#else
+
+size_t GetThreadCount() {
+ // There's no portable way to detect the number of threads, so we just
+ // return 0 to indicate that we cannot detect it.
+ return 0;
+}
+
+#endif // GTEST_OS_MAC
+
+#if GTEST_USES_POSIX_RE
+
+// Implements RE. Currently only needed for death tests.
+
+RE::~RE() {
+ if (is_valid_) {
+ // regfree'ing an invalid regex might crash because the content
+ // of the regex is undefined. Since the regex's are essentially
+ // the same, one cannot be valid (or invalid) without the other
+ // being so too.
+ regfree(&partial_regex_);
+ regfree(&full_regex_);
+ }
+ free(const_cast<char*>(pattern_));
+}
+
+// Returns true iff regular expression re matches the entire str.
+bool RE::FullMatch(const char* str, const RE& re) {
+ if (!re.is_valid_) return false;
+
+ regmatch_t match;
+ return regexec(&re.full_regex_, str, 1, &match, 0) == 0;
+}
+
+// Returns true iff regular expression re matches a substring of str
+// (including str itself).
+bool RE::PartialMatch(const char* str, const RE& re) {
+ if (!re.is_valid_) return false;
+
+ regmatch_t match;
+ return regexec(&re.partial_regex_, str, 1, &match, 0) == 0;
+}
+
+// Initializes an RE from its string representation.
+void RE::Init(const char* regex) {
+ pattern_ = posix::StrDup(regex);
+
+ // Reserves enough bytes to hold the regular expression used for a
+ // full match.
+ const size_t full_regex_len = strlen(regex) + 10;
+ char* const full_pattern = new char[full_regex_len];
+
+ snprintf(full_pattern, full_regex_len, "^(%s)$", regex);
+ is_valid_ = regcomp(&full_regex_, full_pattern, REG_EXTENDED) == 0;
+ // We want to call regcomp(&partial_regex_, ...) even if the
+ // previous expression returns false. Otherwise partial_regex_ may
+ // not be properly initialized can may cause trouble when it's
+ // freed.
+ //
+ // Some implementation of POSIX regex (e.g. on at least some
+ // versions of Cygwin) doesn't accept the empty string as a valid
+ // regex. We change it to an equivalent form "()" to be safe.
+ if (is_valid_) {
+ const char* const partial_regex = (*regex == '\0') ? "()" : regex;
+ is_valid_ = regcomp(&partial_regex_, partial_regex, REG_EXTENDED) == 0;
+ }
+ EXPECT_TRUE(is_valid_)
+ << "Regular expression \"" << regex
+ << "\" is not a valid POSIX Extended regular expression.";
+
+ delete[] full_pattern;
+}
+
+#elif GTEST_USES_SIMPLE_RE
+
+// Returns true iff ch appears anywhere in str (excluding the
+// terminating '\0' character).
+bool IsInSet(char ch, const char* str) {
+ return ch != '\0' && strchr(str, ch) != NULL;
+}
+
+// Returns true iff ch belongs to the given classification. Unlike
+// similar functions in <ctype.h>, these aren't affected by the
+// current locale.
+bool IsAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; }
+bool IsAsciiPunct(char ch) {
+ return IsInSet(ch, "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~");
+}
+bool IsRepeat(char ch) { return IsInSet(ch, "?*+"); }
+bool IsAsciiWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); }
+bool IsAsciiWordChar(char ch) {
+ return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') ||
+ ('0' <= ch && ch <= '9') || ch == '_';
+}
+
+// Returns true iff "\\c" is a supported escape sequence.
+bool IsValidEscape(char c) {
+ return (IsAsciiPunct(c) || IsInSet(c, "dDfnrsStvwW"));
+}
+
+// Returns true iff the given atom (specified by escaped and pattern)
+// matches ch. The result is undefined if the atom is invalid.
+bool AtomMatchesChar(bool escaped, char pattern_char, char ch) {
+ if (escaped) { // "\\p" where p is pattern_char.
+ switch (pattern_char) {
+ case 'd': return IsAsciiDigit(ch);
+ case 'D': return !IsAsciiDigit(ch);
+ case 'f': return ch == '\f';
+ case 'n': return ch == '\n';
+ case 'r': return ch == '\r';
+ case 's': return IsAsciiWhiteSpace(ch);
+ case 'S': return !IsAsciiWhiteSpace(ch);
+ case 't': return ch == '\t';
+ case 'v': return ch == '\v';
+ case 'w': return IsAsciiWordChar(ch);
+ case 'W': return !IsAsciiWordChar(ch);
+ }
+ return IsAsciiPunct(pattern_char) && pattern_char == ch;
+ }
+
+ return (pattern_char == '.' && ch != '\n') || pattern_char == ch;
+}
+
+// Helper function used by ValidateRegex() to format error messages.
+std::string FormatRegexSyntaxError(const char* regex, int index) {
+ return (Message() << "Syntax error at index " << index
+ << " in simple regular expression \"" << regex << "\": ").GetString();
+}
+
+// Generates non-fatal failures and returns false if regex is invalid;
+// otherwise returns true.
+bool ValidateRegex(const char* regex) {
+ if (regex == NULL) {
+ // TODO(wan@google.com): fix the source file location in the
+ // assertion failures to match where the regex is used in user
+ // code.
+ ADD_FAILURE() << "NULL is not a valid simple regular expression.";
+ return false;
+ }
+
+ bool is_valid = true;
+
+ // True iff ?, *, or + can follow the previous atom.
+ bool prev_repeatable = false;
+ for (int i = 0; regex[i]; i++) {
+ if (regex[i] == '\\') { // An escape sequence
+ i++;
+ if (regex[i] == '\0') {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1)
+ << "'\\' cannot appear at the end.";
+ return false;
+ }
+
+ if (!IsValidEscape(regex[i])) {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1)
+ << "invalid escape sequence \"\\" << regex[i] << "\".";
+ is_valid = false;
+ }
+ prev_repeatable = true;
+ } else { // Not an escape sequence.
+ const char ch = regex[i];
+
+ if (ch == '^' && i > 0) {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i)
+ << "'^' can only appear at the beginning.";
+ is_valid = false;
+ } else if (ch == '$' && regex[i + 1] != '\0') {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i)
+ << "'$' can only appear at the end.";
+ is_valid = false;
+ } else if (IsInSet(ch, "()[]{}|")) {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i)
+ << "'" << ch << "' is unsupported.";
+ is_valid = false;
+ } else if (IsRepeat(ch) && !prev_repeatable) {
+ ADD_FAILURE() << FormatRegexSyntaxError(regex, i)
+ << "'" << ch << "' can only follow a repeatable token.";
+ is_valid = false;
+ }
+
+ prev_repeatable = !IsInSet(ch, "^$?*+");
+ }
+ }
+
+ return is_valid;
+}
+
+// Matches a repeated regex atom followed by a valid simple regular
+// expression. The regex atom is defined as c if escaped is false,
+// or \c otherwise. repeat is the repetition meta character (?, *,
+// or +). The behavior is undefined if str contains too many
+// characters to be indexable by size_t, in which case the test will
+// probably time out anyway. We are fine with this limitation as
+// std::string has it too.
+bool MatchRepetitionAndRegexAtHead(
+ bool escaped, char c, char repeat, const char* regex,
+ const char* str) {
+ const size_t min_count = (repeat == '+') ? 1 : 0;
+ const size_t max_count = (repeat == '?') ? 1 :
+ static_cast<size_t>(-1) - 1;
+ // We cannot call numeric_limits::max() as it conflicts with the
+ // max() macro on Windows.
+
+ for (size_t i = 0; i <= max_count; ++i) {
+ // We know that the atom matches each of the first i characters in str.
+ if (i >= min_count && MatchRegexAtHead(regex, str + i)) {
+ // We have enough matches at the head, and the tail matches too.
+ // Since we only care about *whether* the pattern matches str
+ // (as opposed to *how* it matches), there is no need to find a
+ // greedy match.
+ return true;
+ }
+ if (str[i] == '\0' || !AtomMatchesChar(escaped, c, str[i]))
+ return false;
+ }
+ return false;
+}
+
+// Returns true iff regex matches a prefix of str. regex must be a
+// valid simple regular expression and not start with "^", or the
+// result is undefined.
+bool MatchRegexAtHead(const char* regex, const char* str) {
+ if (*regex == '\0') // An empty regex matches a prefix of anything.
+ return true;
+
+ // "$" only matches the end of a string. Note that regex being
+ // valid guarantees that there's nothing after "$" in it.
+ if (*regex == '$')
+ return *str == '\0';
+
+ // Is the first thing in regex an escape sequence?
+ const bool escaped = *regex == '\\';
+ if (escaped)
+ ++regex;
+ if (IsRepeat(regex[1])) {
+ // MatchRepetitionAndRegexAtHead() calls MatchRegexAtHead(), so
+ // here's an indirect recursion. It terminates as the regex gets
+ // shorter in each recursion.
+ return MatchRepetitionAndRegexAtHead(
+ escaped, regex[0], regex[1], regex + 2, str);
+ } else {
+ // regex isn't empty, isn't "$", and doesn't start with a
+ // repetition. We match the first atom of regex with the first
+ // character of str and recurse.
+ return (*str != '\0') && AtomMatchesChar(escaped, *regex, *str) &&
+ MatchRegexAtHead(regex + 1, str + 1);
+ }
+}
+
+// Returns true iff regex matches any substring of str. regex must be
+// a valid simple regular expression, or the result is undefined.
+//
+// The algorithm is recursive, but the recursion depth doesn't exceed
+// the regex length, so we won't need to worry about running out of
+// stack space normally. In rare cases the time complexity can be
+// exponential with respect to the regex length + the string length,
+// but usually it's must faster (often close to linear).
+bool MatchRegexAnywhere(const char* regex, const char* str) {
+ if (regex == NULL || str == NULL)
+ return false;
+
+ if (*regex == '^')
+ return MatchRegexAtHead(regex + 1, str);
+
+ // A successful match can be anywhere in str.
+ do {
+ if (MatchRegexAtHead(regex, str))
+ return true;
+ } while (*str++ != '\0');
+ return false;
+}
+
+// Implements the RE class.
+
+RE::~RE() {
+ free(const_cast<char*>(pattern_));
+ free(const_cast<char*>(full_pattern_));
+}
+
+// Returns true iff regular expression re matches the entire str.
+bool RE::FullMatch(const char* str, const RE& re) {
+ return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str);
+}
+
+// Returns true iff regular expression re matches a substring of str
+// (including str itself).
+bool RE::PartialMatch(const char* str, const RE& re) {
+ return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str);
+}
+
+// Initializes an RE from its string representation.
+void RE::Init(const char* regex) {
+ pattern_ = full_pattern_ = NULL;
+ if (regex != NULL) {
+ pattern_ = posix::StrDup(regex);
+ }
+
+ is_valid_ = ValidateRegex(regex);
+ if (!is_valid_) {
+ // No need to calculate the full pattern when the regex is invalid.
+ return;
+ }
+
+ const size_t len = strlen(regex);
+ // Reserves enough bytes to hold the regular expression used for a
+ // full match: we need space to prepend a '^', append a '$', and
+ // terminate the string with '\0'.
+ char* buffer = static_cast<char*>(malloc(len + 3));
+ full_pattern_ = buffer;
+
+ if (*regex != '^')
+ *buffer++ = '^'; // Makes sure full_pattern_ starts with '^'.
+
+ // We don't use snprintf or strncpy, as they trigger a warning when
+ // compiled with VC++ 8.0.
+ memcpy(buffer, regex, len);
+ buffer += len;
+
+ if (len == 0 || regex[len - 1] != '$')
+ *buffer++ = '$'; // Makes sure full_pattern_ ends with '$'.
+
+ *buffer = '\0';
+}
+
+#endif // GTEST_USES_POSIX_RE
+
+const char kUnknownFile[] = "unknown file";
+
+// Formats a source file path and a line number as they would appear
+// in an error message from the compiler used to compile this code.
+GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) {
+ const std::string file_name(file == NULL ? kUnknownFile : file);
+
+ if (line < 0) {
+ return file_name + ":";
+ }
+#ifdef _MSC_VER
+ return file_name + "(" + StreamableToString(line) + "):";
+#else
+ return file_name + ":" + StreamableToString(line) + ":";
+#endif // _MSC_VER
+}
+
+// Formats a file location for compiler-independent XML output.
+// Although this function is not platform dependent, we put it next to
+// FormatFileLocation in order to contrast the two functions.
+// Note that FormatCompilerIndependentFileLocation() does NOT append colon
+// to the file location it produces, unlike FormatFileLocation().
+GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(
+ const char* file, int line) {
+ const std::string file_name(file == NULL ? kUnknownFile : file);
+
+ if (line < 0)
+ return file_name;
+ else
+ return file_name + ":" + StreamableToString(line);
+}
+
+
+GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line)
+ : severity_(severity) {
+ const char* const marker =
+ severity == GTEST_INFO ? "[ INFO ]" :
+ severity == GTEST_WARNING ? "[WARNING]" :
+ severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]";
+ GetStream() << ::std::endl << marker << " "
+ << FormatFileLocation(file, line).c_str() << ": ";
+}
+
+// Flushes the buffers and, if severity is GTEST_FATAL, aborts the program.
+GTestLog::~GTestLog() {
+ GetStream() << ::std::endl;
+ if (severity_ == GTEST_FATAL) {
+ fflush(stderr);
+ posix::Abort();
+ }
+}
+// Disable Microsoft deprecation warnings for POSIX functions called from
+// this class (creat, dup, dup2, and close)
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable: 4996)
+#endif // _MSC_VER
+
+#if GTEST_HAS_STREAM_REDIRECTION
+
+// Object that captures an output stream (stdout/stderr).
+class CapturedStream {
+ public:
+ // The ctor redirects the stream to a temporary file.
+ explicit CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) {
+# if GTEST_OS_WINDOWS
+ char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT
+ char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT
+
+ ::GetTempPathA(sizeof(temp_dir_path), temp_dir_path);
+ const UINT success = ::GetTempFileNameA(temp_dir_path,
+ "gtest_redir",
+ 0, // Generate unique file name.
+ temp_file_path);
+ GTEST_CHECK_(success != 0)
+ << "Unable to create a temporary file in " << temp_dir_path;
+ const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE);
+ GTEST_CHECK_(captured_fd != -1) << "Unable to open temporary file "
+ << temp_file_path;
+ filename_ = temp_file_path;
+# else
+ // There's no guarantee that a test has write access to the current
+ // directory, so we create the temporary file in the /tmp directory
+ // instead. We use /tmp on most systems, and /sdcard on Android.
+ // That's because Android doesn't have /tmp.
+# if GTEST_OS_LINUX_ANDROID
+ // Note: Android applications are expected to call the framework's
+ // Context.getExternalStorageDirectory() method through JNI to get
+ // the location of the world-writable SD Card directory. However,
+ // this requires a Context handle, which cannot be retrieved
+ // globally from native code. Doing so also precludes running the
+ // code as part of a regular standalone executable, which doesn't
+ // run in a Dalvik process (e.g. when running it through 'adb shell').
+ //
+ // The location /sdcard is directly accessible from native code
+ // and is the only location (unofficially) supported by the Android
+ // team. It's generally a symlink to the real SD Card mount point
+ // which can be /mnt/sdcard, /mnt/sdcard0, /system/media/sdcard, or
+ // other OEM-customized locations. Never rely on these, and always
+ // use /sdcard.
+ char name_template[] = "/sdcard/gtest_captured_stream.XXXXXX";
+# else
+ char name_template[] = "/tmp/captured_stream.XXXXXX";
+# endif // GTEST_OS_LINUX_ANDROID
+ const int captured_fd = mkstemp(name_template);
+ filename_ = name_template;
+# endif // GTEST_OS_WINDOWS
+ fflush(NULL);
+ dup2(captured_fd, fd_);
+ close(captured_fd);
+ }
+
+ ~CapturedStream() {
+ remove(filename_.c_str());
+ }
+
+ std::string GetCapturedString() {
+ if (uncaptured_fd_ != -1) {
+ // Restores the original stream.
+ fflush(NULL);
+ dup2(uncaptured_fd_, fd_);
+ close(uncaptured_fd_);
+ uncaptured_fd_ = -1;
+ }
+
+ FILE* const file = posix::FOpen(filename_.c_str(), "r");
+ const std::string content = ReadEntireFile(file);
+ posix::FClose(file);
+ return content;
+ }
+
+ private:
+ // Reads the entire content of a file as an std::string.
+ static std::string ReadEntireFile(FILE* file);
+
+ // Returns the size (in bytes) of a file.
+ static size_t GetFileSize(FILE* file);
+
+ const int fd_; // A stream to capture.
+ int uncaptured_fd_;
+ // Name of the temporary file holding the stderr output.
+ ::std::string filename_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream);
+};
+
+// Returns the size (in bytes) of a file.
+size_t CapturedStream::GetFileSize(FILE* file) {
+ fseek(file, 0, SEEK_END);
+ return static_cast<size_t>(ftell(file));
+}
+
+// Reads the entire content of a file as a string.
+std::string CapturedStream::ReadEntireFile(FILE* file) {
+ const size_t file_size = GetFileSize(file);
+ char* const buffer = new char[file_size];
+
+ size_t bytes_last_read = 0; // # of bytes read in the last fread()
+ size_t bytes_read = 0; // # of bytes read so far
+
+ fseek(file, 0, SEEK_SET);
+
+ // Keeps reading the file until we cannot read further or the
+ // pre-determined file size is reached.
+ do {
+ bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file);
+ bytes_read += bytes_last_read;
+ } while (bytes_last_read > 0 && bytes_read < file_size);
+
+ const std::string content(buffer, bytes_read);
+ delete[] buffer;
+
+ return content;
+}
+
+# ifdef _MSC_VER
+# pragma warning(pop)
+# endif // _MSC_VER
+
+static CapturedStream* g_captured_stderr = NULL;
+static CapturedStream* g_captured_stdout = NULL;
+
+// Starts capturing an output stream (stdout/stderr).
+void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) {
+ if (*stream != NULL) {
+ GTEST_LOG_(FATAL) << "Only one " << stream_name
+ << " capturer can exist at a time.";
+ }
+ *stream = new CapturedStream(fd);
+}
+
+// Stops capturing the output stream and returns the captured string.
+std::string GetCapturedStream(CapturedStream** captured_stream) {
+ const std::string content = (*captured_stream)->GetCapturedString();
+
+ delete *captured_stream;
+ *captured_stream = NULL;
+
+ return content;
+}
+
+// Starts capturing stdout.
+void CaptureStdout() {
+ CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout);
+}
+
+// Starts capturing stderr.
+void CaptureStderr() {
+ CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr);
+}
+
+// Stops capturing stdout and returns the captured string.
+std::string GetCapturedStdout() {
+ return GetCapturedStream(&g_captured_stdout);
+}
+
+// Stops capturing stderr and returns the captured string.
+std::string GetCapturedStderr() {
+ return GetCapturedStream(&g_captured_stderr);
+}
+
+#endif // GTEST_HAS_STREAM_REDIRECTION
+
+#if GTEST_HAS_DEATH_TEST
+
+// A copy of all command line arguments. Set by InitGoogleTest().
+::std::vector<testing::internal::string> g_argvs;
+
+static const ::std::vector<testing::internal::string>* g_injected_test_argvs =
+ NULL; // Owned.
+
+void SetInjectableArgvs(const ::std::vector<testing::internal::string>* argvs) {
+ if (g_injected_test_argvs != argvs)
+ delete g_injected_test_argvs;
+ g_injected_test_argvs = argvs;
+}
+
+const ::std::vector<testing::internal::string>& GetInjectableArgvs() {
+ if (g_injected_test_argvs != NULL) {
+ return *g_injected_test_argvs;
+ }
+ return g_argvs;
+}
+#endif // GTEST_HAS_DEATH_TEST
+
+#if GTEST_OS_WINDOWS_MOBILE
+namespace posix {
+void Abort() {
+ DebugBreak();
+ TerminateProcess(GetCurrentProcess(), 1);
+}
+} // namespace posix
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+// Returns the name of the environment variable corresponding to the
+// given flag. For example, FlagToEnvVar("foo") will return
+// "GTEST_FOO" in the open-source version.
+static std::string FlagToEnvVar(const char* flag) {
+ const std::string full_flag =
+ (Message() << GTEST_FLAG_PREFIX_ << flag).GetString();
+
+ Message env_var;
+ for (size_t i = 0; i != full_flag.length(); i++) {
+ env_var << ToUpper(full_flag.c_str()[i]);
+ }
+
+ return env_var.GetString();
+}
+
+// Parses 'str' for a 32-bit signed integer. If successful, writes
+// the result to *value and returns true; otherwise leaves *value
+// unchanged and returns false.
+bool ParseInt32(const Message& src_text, const char* str, Int32* value) {
+ // Parses the environment variable as a decimal integer.
+ char* end = NULL;
+ const long long_value = strtol(str, &end, 10); // NOLINT
+
+ // Has strtol() consumed all characters in the string?
+ if (*end != '\0') {
+ // No - an invalid character was encountered.
+ Message msg;
+ msg << "WARNING: " << src_text
+ << " is expected to be a 32-bit integer, but actually"
+ << " has value \"" << str << "\".\n";
+ printf("%s", msg.GetString().c_str());
+ fflush(stdout);
+ return false;
+ }
+
+ // Is the parsed value in the range of an Int32?
+ const Int32 result = static_cast<Int32>(long_value);
+ if (long_value == LONG_MAX || long_value == LONG_MIN ||
+ // The parsed value overflows as a long. (strtol() returns
+ // LONG_MAX or LONG_MIN when the input overflows.)
+ result != long_value
+ // The parsed value overflows as an Int32.
+ ) {
+ Message msg;
+ msg << "WARNING: " << src_text
+ << " is expected to be a 32-bit integer, but actually"
+ << " has value " << str << ", which overflows.\n";
+ printf("%s", msg.GetString().c_str());
+ fflush(stdout);
+ return false;
+ }
+
+ *value = result;
+ return true;
+}
+
+// Reads and returns the Boolean environment variable corresponding to
+// the given flag; if it's not set, returns default_value.
+//
+// The value is considered true iff it's not "0".
+bool BoolFromGTestEnv(const char* flag, bool default_value) {
+ const std::string env_var = FlagToEnvVar(flag);
+ const char* const string_value = posix::GetEnv(env_var.c_str());
+ return string_value == NULL ?
+ default_value : strcmp(string_value, "0") != 0;
+}
+
+// Reads and returns a 32-bit integer stored in the environment
+// variable corresponding to the given flag; if it isn't set or
+// doesn't represent a valid 32-bit integer, returns default_value.
+Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) {
+ const std::string env_var = FlagToEnvVar(flag);
+ const char* const string_value = posix::GetEnv(env_var.c_str());
+ if (string_value == NULL) {
+ // The environment variable is not set.
+ return default_value;
+ }
+
+ Int32 result = default_value;
+ if (!ParseInt32(Message() << "Environment variable " << env_var,
+ string_value, &result)) {
+ printf("The default value %s is used.\n",
+ (Message() << default_value).GetString().c_str());
+ fflush(stdout);
+ return default_value;
+ }
+
+ return result;
+}
+
+// Reads and returns the string environment variable corresponding to
+// the given flag; if it's not set, returns default_value.
+const char* StringFromGTestEnv(const char* flag, const char* default_value) {
+ const std::string env_var = FlagToEnvVar(flag);
+ const char* const value = posix::GetEnv(env_var.c_str());
+ return value == NULL ? default_value : value;
+}
+
+} // namespace internal
+} // namespace testing
diff --git a/extern/gtest/src/gtest-printers.cc b/extern/gtest/src/gtest-printers.cc
new file mode 100644
index 00000000000..75fa4081009
--- /dev/null
+++ b/extern/gtest/src/gtest-printers.cc
@@ -0,0 +1,363 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Test - The Google C++ Testing Framework
+//
+// This file implements a universal value printer that can print a
+// value of any type T:
+//
+// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
+//
+// It uses the << operator when possible, and prints the bytes in the
+// object otherwise. A user can override its behavior for a class
+// type Foo by defining either operator<<(::std::ostream&, const Foo&)
+// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that
+// defines Foo.
+
+#include "gtest/gtest-printers.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <ostream> // NOLINT
+#include <string>
+#include "gtest/internal/gtest-port.h"
+
+namespace testing {
+
+namespace {
+
+using ::std::ostream;
+
+// Prints a segment of bytes in the given object.
+void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start,
+ size_t count, ostream* os) {
+ char text[5] = "";
+ for (size_t i = 0; i != count; i++) {
+ const size_t j = start + i;
+ if (i != 0) {
+ // Organizes the bytes into groups of 2 for easy parsing by
+ // human.
+ if ((j % 2) == 0)
+ *os << ' ';
+ else
+ *os << '-';
+ }
+ GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]);
+ *os << text;
+ }
+}
+
+// Prints the bytes in the given value to the given ostream.
+void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count,
+ ostream* os) {
+ // Tells the user how big the object is.
+ *os << count << "-byte object <";
+
+ const size_t kThreshold = 132;
+ const size_t kChunkSize = 64;
+ // If the object size is bigger than kThreshold, we'll have to omit
+ // some details by printing only the first and the last kChunkSize
+ // bytes.
+ // TODO(wan): let the user control the threshold using a flag.
+ if (count < kThreshold) {
+ PrintByteSegmentInObjectTo(obj_bytes, 0, count, os);
+ } else {
+ PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os);
+ *os << " ... ";
+ // Rounds up to 2-byte boundary.
+ const size_t resume_pos = (count - kChunkSize + 1)/2*2;
+ PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os);
+ }
+ *os << ">";
+}
+
+} // namespace
+
+namespace internal2 {
+
+// Delegates to PrintBytesInObjectToImpl() to print the bytes in the
+// given object. The delegation simplifies the implementation, which
+// uses the << operator and thus is easier done outside of the
+// ::testing::internal namespace, which contains a << operator that
+// sometimes conflicts with the one in STL.
+void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count,
+ ostream* os) {
+ PrintBytesInObjectToImpl(obj_bytes, count, os);
+}
+
+} // namespace internal2
+
+namespace internal {
+
+// Depending on the value of a char (or wchar_t), we print it in one
+// of three formats:
+// - as is if it's a printable ASCII (e.g. 'a', '2', ' '),
+// - as a hexidecimal escape sequence (e.g. '\x7F'), or
+// - as a special escape sequence (e.g. '\r', '\n').
+enum CharFormat {
+ kAsIs,
+ kHexEscape,
+ kSpecialEscape
+};
+
+// Returns true if c is a printable ASCII character. We test the
+// value of c directly instead of calling isprint(), which is buggy on
+// Windows Mobile.
+inline bool IsPrintableAscii(wchar_t c) {
+ return 0x20 <= c && c <= 0x7E;
+}
+
+// Prints a wide or narrow char c as a character literal without the
+// quotes, escaping it when necessary; returns how c was formatted.
+// The template argument UnsignedChar is the unsigned version of Char,
+// which is the type of c.
+template <typename UnsignedChar, typename Char>
+static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) {
+ switch (static_cast<wchar_t>(c)) {
+ case L'\0':
+ *os << "\\0";
+ break;
+ case L'\'':
+ *os << "\\'";
+ break;
+ case L'\\':
+ *os << "\\\\";
+ break;
+ case L'\a':
+ *os << "\\a";
+ break;
+ case L'\b':
+ *os << "\\b";
+ break;
+ case L'\f':
+ *os << "\\f";
+ break;
+ case L'\n':
+ *os << "\\n";
+ break;
+ case L'\r':
+ *os << "\\r";
+ break;
+ case L'\t':
+ *os << "\\t";
+ break;
+ case L'\v':
+ *os << "\\v";
+ break;
+ default:
+ if (IsPrintableAscii(c)) {
+ *os << static_cast<char>(c);
+ return kAsIs;
+ } else {
+ *os << "\\x" + String::FormatHexInt(static_cast<UnsignedChar>(c));
+ return kHexEscape;
+ }
+ }
+ return kSpecialEscape;
+}
+
+// Prints a wchar_t c as if it's part of a string literal, escaping it when
+// necessary; returns how c was formatted.
+static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) {
+ switch (c) {
+ case L'\'':
+ *os << "'";
+ return kAsIs;
+ case L'"':
+ *os << "\\\"";
+ return kSpecialEscape;
+ default:
+ return PrintAsCharLiteralTo<wchar_t>(c, os);
+ }
+}
+
+// Prints a char c as if it's part of a string literal, escaping it when
+// necessary; returns how c was formatted.
+static CharFormat PrintAsStringLiteralTo(char c, ostream* os) {
+ return PrintAsStringLiteralTo(
+ static_cast<wchar_t>(static_cast<unsigned char>(c)), os);
+}
+
+// Prints a wide or narrow character c and its code. '\0' is printed
+// as "'\\0'", other unprintable characters are also properly escaped
+// using the standard C++ escape sequence. The template argument
+// UnsignedChar is the unsigned version of Char, which is the type of c.
+template <typename UnsignedChar, typename Char>
+void PrintCharAndCodeTo(Char c, ostream* os) {
+ // First, print c as a literal in the most readable form we can find.
+ *os << ((sizeof(c) > 1) ? "L'" : "'");
+ const CharFormat format = PrintAsCharLiteralTo<UnsignedChar>(c, os);
+ *os << "'";
+
+ // To aid user debugging, we also print c's code in decimal, unless
+ // it's 0 (in which case c was printed as '\\0', making the code
+ // obvious).
+ if (c == 0)
+ return;
+ *os << " (" << static_cast<int>(c);
+
+ // For more convenience, we print c's code again in hexidecimal,
+ // unless c was already printed in the form '\x##' or the code is in
+ // [1, 9].
+ if (format == kHexEscape || (1 <= c && c <= 9)) {
+ // Do nothing.
+ } else {
+ *os << ", 0x" << String::FormatHexInt(static_cast<UnsignedChar>(c));
+ }
+ *os << ")";
+}
+
+void PrintTo(unsigned char c, ::std::ostream* os) {
+ PrintCharAndCodeTo<unsigned char>(c, os);
+}
+void PrintTo(signed char c, ::std::ostream* os) {
+ PrintCharAndCodeTo<unsigned char>(c, os);
+}
+
+// Prints a wchar_t as a symbol if it is printable or as its internal
+// code otherwise and also as its code. L'\0' is printed as "L'\\0'".
+void PrintTo(wchar_t wc, ostream* os) {
+ PrintCharAndCodeTo<wchar_t>(wc, os);
+}
+
+// Prints the given array of characters to the ostream. CharType must be either
+// char or wchar_t.
+// The array starts at begin, the length is len, it may include '\0' characters
+// and may not be NUL-terminated.
+template <typename CharType>
+static void PrintCharsAsStringTo(
+ const CharType* begin, size_t len, ostream* os) {
+ const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\"";
+ *os << kQuoteBegin;
+ bool is_previous_hex = false;
+ for (size_t index = 0; index < len; ++index) {
+ const CharType cur = begin[index];
+ if (is_previous_hex && IsXDigit(cur)) {
+ // Previous character is of '\x..' form and this character can be
+ // interpreted as another hexadecimal digit in its number. Break string to
+ // disambiguate.
+ *os << "\" " << kQuoteBegin;
+ }
+ is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape;
+ }
+ *os << "\"";
+}
+
+// Prints a (const) char/wchar_t array of 'len' elements, starting at address
+// 'begin'. CharType must be either char or wchar_t.
+template <typename CharType>
+static void UniversalPrintCharArray(
+ const CharType* begin, size_t len, ostream* os) {
+ // The code
+ // const char kFoo[] = "foo";
+ // generates an array of 4, not 3, elements, with the last one being '\0'.
+ //
+ // Therefore when printing a char array, we don't print the last element if
+ // it's '\0', such that the output matches the string literal as it's
+ // written in the source code.
+ if (len > 0 && begin[len - 1] == '\0') {
+ PrintCharsAsStringTo(begin, len - 1, os);
+ return;
+ }
+
+ // If, however, the last element in the array is not '\0', e.g.
+ // const char kFoo[] = { 'f', 'o', 'o' };
+ // we must print the entire array. We also print a message to indicate
+ // that the array is not NUL-terminated.
+ PrintCharsAsStringTo(begin, len, os);
+ *os << " (no terminating NUL)";
+}
+
+// Prints a (const) char array of 'len' elements, starting at address 'begin'.
+void UniversalPrintArray(const char* begin, size_t len, ostream* os) {
+ UniversalPrintCharArray(begin, len, os);
+}
+
+// Prints a (const) wchar_t array of 'len' elements, starting at address
+// 'begin'.
+void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) {
+ UniversalPrintCharArray(begin, len, os);
+}
+
+// Prints the given C string to the ostream.
+void PrintTo(const char* s, ostream* os) {
+ if (s == NULL) {
+ *os << "NULL";
+ } else {
+ *os << ImplicitCast_<const void*>(s) << " pointing to ";
+ PrintCharsAsStringTo(s, strlen(s), os);
+ }
+}
+
+// MSVC compiler can be configured to define whar_t as a typedef
+// of unsigned short. Defining an overload for const wchar_t* in that case
+// would cause pointers to unsigned shorts be printed as wide strings,
+// possibly accessing more memory than intended and causing invalid
+// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when
+// wchar_t is implemented as a native type.
+#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
+// Prints the given wide C string to the ostream.
+void PrintTo(const wchar_t* s, ostream* os) {
+ if (s == NULL) {
+ *os << "NULL";
+ } else {
+ *os << ImplicitCast_<const void*>(s) << " pointing to ";
+ PrintCharsAsStringTo(s, wcslen(s), os);
+ }
+}
+#endif // wchar_t is native
+
+// Prints a ::string object.
+#if GTEST_HAS_GLOBAL_STRING
+void PrintStringTo(const ::string& s, ostream* os) {
+ PrintCharsAsStringTo(s.data(), s.size(), os);
+}
+#endif // GTEST_HAS_GLOBAL_STRING
+
+void PrintStringTo(const ::std::string& s, ostream* os) {
+ PrintCharsAsStringTo(s.data(), s.size(), os);
+}
+
+// Prints a ::wstring object.
+#if GTEST_HAS_GLOBAL_WSTRING
+void PrintWideStringTo(const ::wstring& s, ostream* os) {
+ PrintCharsAsStringTo(s.data(), s.size(), os);
+}
+#endif // GTEST_HAS_GLOBAL_WSTRING
+
+#if GTEST_HAS_STD_WSTRING
+void PrintWideStringTo(const ::std::wstring& s, ostream* os) {
+ PrintCharsAsStringTo(s.data(), s.size(), os);
+}
+#endif // GTEST_HAS_STD_WSTRING
+
+} // namespace internal
+
+} // namespace testing
diff --git a/extern/gtest/src/gtest-test-part.cc b/extern/gtest/src/gtest-test-part.cc
new file mode 100644
index 00000000000..c60eef3ab35
--- /dev/null
+++ b/extern/gtest/src/gtest-test-part.cc
@@ -0,0 +1,110 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: mheule@google.com (Markus Heule)
+//
+// The Google C++ Testing Framework (Google Test)
+
+#include "gtest/gtest-test-part.h"
+
+// Indicates that this translation unit is part of Google Test's
+// implementation. It must come before gtest-internal-inl.h is
+// included, or there will be a compiler error. This trick is to
+// prevent a user from accidentally including gtest-internal-inl.h in
+// his code.
+#define GTEST_IMPLEMENTATION_ 1
+#include "src/gtest-internal-inl.h"
+#undef GTEST_IMPLEMENTATION_
+
+namespace testing {
+
+using internal::GetUnitTestImpl;
+
+// Gets the summary of the failure message by omitting the stack trace
+// in it.
+std::string TestPartResult::ExtractSummary(const char* message) {
+ const char* const stack_trace = strstr(message, internal::kStackTraceMarker);
+ return stack_trace == NULL ? message :
+ std::string(message, stack_trace);
+}
+
+// Prints a TestPartResult object.
+std::ostream& operator<<(std::ostream& os, const TestPartResult& result) {
+ return os
+ << result.file_name() << ":" << result.line_number() << ": "
+ << (result.type() == TestPartResult::kSuccess ? "Success" :
+ result.type() == TestPartResult::kFatalFailure ? "Fatal failure" :
+ "Non-fatal failure") << ":\n"
+ << result.message() << std::endl;
+}
+
+// Appends a TestPartResult to the array.
+void TestPartResultArray::Append(const TestPartResult& result) {
+ array_.push_back(result);
+}
+
+// Returns the TestPartResult at the given index (0-based).
+const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const {
+ if (index < 0 || index >= size()) {
+ printf("\nInvalid index (%d) into TestPartResultArray.\n", index);
+ internal::posix::Abort();
+ }
+
+ return array_[index];
+}
+
+// Returns the number of TestPartResult objects in the array.
+int TestPartResultArray::size() const {
+ return static_cast<int>(array_.size());
+}
+
+namespace internal {
+
+HasNewFatalFailureHelper::HasNewFatalFailureHelper()
+ : has_new_fatal_failure_(false),
+ original_reporter_(GetUnitTestImpl()->
+ GetTestPartResultReporterForCurrentThread()) {
+ GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this);
+}
+
+HasNewFatalFailureHelper::~HasNewFatalFailureHelper() {
+ GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(
+ original_reporter_);
+}
+
+void HasNewFatalFailureHelper::ReportTestPartResult(
+ const TestPartResult& result) {
+ if (result.fatally_failed())
+ has_new_fatal_failure_ = true;
+ original_reporter_->ReportTestPartResult(result);
+}
+
+} // namespace internal
+
+} // namespace testing
diff --git a/extern/gtest/src/gtest-typed-test.cc b/extern/gtest/src/gtest-typed-test.cc
new file mode 100644
index 00000000000..f0079f407c5
--- /dev/null
+++ b/extern/gtest/src/gtest-typed-test.cc
@@ -0,0 +1,110 @@
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+#include "gtest/gtest-typed-test.h"
+#include "gtest/gtest.h"
+
+namespace testing {
+namespace internal {
+
+#if GTEST_HAS_TYPED_TEST_P
+
+// Skips to the first non-space char in str. Returns an empty string if str
+// contains only whitespace characters.
+static const char* SkipSpaces(const char* str) {
+ while (IsSpace(*str))
+ str++;
+ return str;
+}
+
+// Verifies that registered_tests match the test names in
+// defined_test_names_; returns registered_tests if successful, or
+// aborts the program otherwise.
+const char* TypedTestCasePState::VerifyRegisteredTestNames(
+ const char* file, int line, const char* registered_tests) {
+ typedef ::std::set<const char*>::const_iterator DefinedTestIter;
+ registered_ = true;
+
+ // Skip initial whitespace in registered_tests since some
+ // preprocessors prefix stringizied literals with whitespace.
+ registered_tests = SkipSpaces(registered_tests);
+
+ Message errors;
+ ::std::set<std::string> tests;
+ for (const char* names = registered_tests; names != NULL;
+ names = SkipComma(names)) {
+ const std::string name = GetPrefixUntilComma(names);
+ if (tests.count(name) != 0) {
+ errors << "Test " << name << " is listed more than once.\n";
+ continue;
+ }
+
+ bool found = false;
+ for (DefinedTestIter it = defined_test_names_.begin();
+ it != defined_test_names_.end();
+ ++it) {
+ if (name == *it) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ tests.insert(name);
+ } else {
+ errors << "No test named " << name
+ << " can be found in this test case.\n";
+ }
+ }
+
+ for (DefinedTestIter it = defined_test_names_.begin();
+ it != defined_test_names_.end();
+ ++it) {
+ if (tests.count(*it) == 0) {
+ errors << "You forgot to list test " << *it << ".\n";
+ }
+ }
+
+ const std::string& errors_str = errors.GetString();
+ if (errors_str != "") {
+ fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(),
+ errors_str.c_str());
+ fflush(stderr);
+ posix::Abort();
+ }
+
+ return registered_tests;
+}
+
+#endif // GTEST_HAS_TYPED_TEST_P
+
+} // namespace internal
+} // namespace testing
diff --git a/extern/gtest/src/gtest.cc b/extern/gtest/src/gtest.cc
new file mode 100644
index 00000000000..6de53dd0198
--- /dev/null
+++ b/extern/gtest/src/gtest.cc
@@ -0,0 +1,5015 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+//
+// The Google C++ Testing Framework (Google Test)
+
+#include "gtest/gtest.h"
+#include "gtest/gtest-spi.h"
+
+#include <ctype.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include <algorithm>
+#include <iomanip>
+#include <limits>
+#include <ostream> // NOLINT
+#include <sstream>
+#include <vector>
+
+#if GTEST_OS_LINUX
+
+// TODO(kenton@google.com): Use autoconf to detect availability of
+// gettimeofday().
+# define GTEST_HAS_GETTIMEOFDAY_ 1
+
+# include <fcntl.h> // NOLINT
+# include <limits.h> // NOLINT
+# include <sched.h> // NOLINT
+// Declares vsnprintf(). This header is not available on Windows.
+# include <strings.h> // NOLINT
+# include <sys/mman.h> // NOLINT
+# include <sys/time.h> // NOLINT
+# include <unistd.h> // NOLINT
+# include <string>
+
+#elif GTEST_OS_SYMBIAN
+# define GTEST_HAS_GETTIMEOFDAY_ 1
+# include <sys/time.h> // NOLINT
+
+#elif GTEST_OS_ZOS
+# define GTEST_HAS_GETTIMEOFDAY_ 1
+# include <sys/time.h> // NOLINT
+
+// On z/OS we additionally need strings.h for strcasecmp.
+# include <strings.h> // NOLINT
+
+#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE.
+
+# include <windows.h> // NOLINT
+
+#elif GTEST_OS_WINDOWS // We are on Windows proper.
+
+# include <io.h> // NOLINT
+# include <sys/timeb.h> // NOLINT
+# include <sys/types.h> // NOLINT
+# include <sys/stat.h> // NOLINT
+
+# if GTEST_OS_WINDOWS_MINGW
+// MinGW has gettimeofday() but not _ftime64().
+// TODO(kenton@google.com): Use autoconf to detect availability of
+// gettimeofday().
+// TODO(kenton@google.com): There are other ways to get the time on
+// Windows, like GetTickCount() or GetSystemTimeAsFileTime(). MinGW
+// supports these. consider using them instead.
+# define GTEST_HAS_GETTIMEOFDAY_ 1
+# include <sys/time.h> // NOLINT
+# endif // GTEST_OS_WINDOWS_MINGW
+
+// cpplint thinks that the header is already included, so we want to
+// silence it.
+# include <windows.h> // NOLINT
+
+#else
+
+// Assume other platforms have gettimeofday().
+// TODO(kenton@google.com): Use autoconf to detect availability of
+// gettimeofday().
+# define GTEST_HAS_GETTIMEOFDAY_ 1
+
+// cpplint thinks that the header is already included, so we want to
+// silence it.
+# include <sys/time.h> // NOLINT
+# include <unistd.h> // NOLINT
+
+#endif // GTEST_OS_LINUX
+
+#if GTEST_HAS_EXCEPTIONS
+# include <stdexcept>
+#endif
+
+#if GTEST_CAN_STREAM_RESULTS_
+# include <arpa/inet.h> // NOLINT
+# include <netdb.h> // NOLINT
+#endif
+
+// Indicates that this translation unit is part of Google Test's
+// implementation. It must come before gtest-internal-inl.h is
+// included, or there will be a compiler error. This trick is to
+// prevent a user from accidentally including gtest-internal-inl.h in
+// his code.
+#define GTEST_IMPLEMENTATION_ 1
+#include "src/gtest-internal-inl.h"
+#undef GTEST_IMPLEMENTATION_
+
+#if GTEST_OS_WINDOWS
+# define vsnprintf _vsnprintf
+#endif // GTEST_OS_WINDOWS
+
+namespace testing {
+
+using internal::CountIf;
+using internal::ForEach;
+using internal::GetElementOr;
+using internal::Shuffle;
+
+// Constants.
+
+// A test whose test case name or test name matches this filter is
+// disabled and not run.
+static const char kDisableTestFilter[] = "DISABLED_*:*/DISABLED_*";
+
+// A test case whose name matches this filter is considered a death
+// test case and will be run before test cases whose name doesn't
+// match this filter.
+static const char kDeathTestCaseFilter[] = "*DeathTest:*DeathTest/*";
+
+// A test filter that matches everything.
+static const char kUniversalFilter[] = "*";
+
+// The default output file for XML output.
+static const char kDefaultOutputFile[] = "test_detail.xml";
+
+// The environment variable name for the test shard index.
+static const char kTestShardIndex[] = "GTEST_SHARD_INDEX";
+// The environment variable name for the total number of test shards.
+static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS";
+// The environment variable name for the test shard status file.
+static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE";
+
+namespace internal {
+
+// The text used in failure messages to indicate the start of the
+// stack trace.
+const char kStackTraceMarker[] = "\nStack trace:\n";
+
+// g_help_flag is true iff the --help flag or an equivalent form is
+// specified on the command line.
+bool g_help_flag = false;
+
+} // namespace internal
+
+static const char* GetDefaultFilter() {
+ return kUniversalFilter;
+}
+
+GTEST_DEFINE_bool_(
+ also_run_disabled_tests,
+ internal::BoolFromGTestEnv("also_run_disabled_tests", false),
+ "Run disabled tests too, in addition to the tests normally being run.");
+
+GTEST_DEFINE_bool_(
+ break_on_failure,
+ internal::BoolFromGTestEnv("break_on_failure", false),
+ "True iff a failed assertion should be a debugger break-point.");
+
+GTEST_DEFINE_bool_(
+ catch_exceptions,
+ internal::BoolFromGTestEnv("catch_exceptions", true),
+ "True iff " GTEST_NAME_
+ " should catch exceptions and treat them as test failures.");
+
+GTEST_DEFINE_string_(
+ color,
+ internal::StringFromGTestEnv("color", "auto"),
+ "Whether to use colors in the output. Valid values: yes, no, "
+ "and auto. 'auto' means to use colors if the output is "
+ "being sent to a terminal and the TERM environment variable "
+ "is set to a terminal type that supports colors.");
+
+GTEST_DEFINE_string_(
+ filter,
+ internal::StringFromGTestEnv("filter", GetDefaultFilter()),
+ "A colon-separated list of glob (not regex) patterns "
+ "for filtering the tests to run, optionally followed by a "
+ "'-' and a : separated list of negative patterns (tests to "
+ "exclude). A test is run if it matches one of the positive "
+ "patterns and does not match any of the negative patterns.");
+
+GTEST_DEFINE_bool_(list_tests, false,
+ "List all tests without running them.");
+
+GTEST_DEFINE_string_(
+ output,
+ internal::StringFromGTestEnv("output", ""),
+ "A format (currently must be \"xml\"), optionally followed "
+ "by a colon and an output file name or directory. A directory "
+ "is indicated by a trailing pathname separator. "
+ "Examples: \"xml:filename.xml\", \"xml::directoryname/\". "
+ "If a directory is specified, output files will be created "
+ "within that directory, with file-names based on the test "
+ "executable's name and, if necessary, made unique by adding "
+ "digits.");
+
+GTEST_DEFINE_bool_(
+ print_time,
+ internal::BoolFromGTestEnv("print_time", true),
+ "True iff " GTEST_NAME_
+ " should display elapsed time in text output.");
+
+GTEST_DEFINE_int32_(
+ random_seed,
+ internal::Int32FromGTestEnv("random_seed", 0),
+ "Random number seed to use when shuffling test orders. Must be in range "
+ "[1, 99999], or 0 to use a seed based on the current time.");
+
+GTEST_DEFINE_int32_(
+ repeat,
+ internal::Int32FromGTestEnv("repeat", 1),
+ "How many times to repeat each test. Specify a negative number "
+ "for repeating forever. Useful for shaking out flaky tests.");
+
+GTEST_DEFINE_bool_(
+ show_internal_stack_frames, false,
+ "True iff " GTEST_NAME_ " should include internal stack frames when "
+ "printing test failure stack traces.");
+
+GTEST_DEFINE_bool_(
+ shuffle,
+ internal::BoolFromGTestEnv("shuffle", false),
+ "True iff " GTEST_NAME_
+ " should randomize tests' order on every run.");
+
+GTEST_DEFINE_int32_(
+ stack_trace_depth,
+ internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth),
+ "The maximum number of stack frames to print when an "
+ "assertion fails. The valid range is 0 through 100, inclusive.");
+
+GTEST_DEFINE_string_(
+ stream_result_to,
+ internal::StringFromGTestEnv("stream_result_to", ""),
+ "This flag specifies the host name and the port number on which to stream "
+ "test results. Example: \"localhost:555\". The flag is effective only on "
+ "Linux.");
+
+GTEST_DEFINE_bool_(
+ throw_on_failure,
+ internal::BoolFromGTestEnv("throw_on_failure", false),
+ "When this flag is specified, a failed assertion will throw an exception "
+ "if exceptions are enabled or exit the program with a non-zero code "
+ "otherwise.");
+
+namespace internal {
+
+// Generates a random number from [0, range), using a Linear
+// Congruential Generator (LCG). Crashes if 'range' is 0 or greater
+// than kMaxRange.
+UInt32 Random::Generate(UInt32 range) {
+ // These constants are the same as are used in glibc's rand(3).
+ state_ = (1103515245U*state_ + 12345U) % kMaxRange;
+
+ GTEST_CHECK_(range > 0)
+ << "Cannot generate a number in the range [0, 0).";
+ GTEST_CHECK_(range <= kMaxRange)
+ << "Generation of a number in [0, " << range << ") was requested, "
+ << "but this can only generate numbers in [0, " << kMaxRange << ").";
+
+ // Converting via modulus introduces a bit of downward bias, but
+ // it's simple, and a linear congruential generator isn't too good
+ // to begin with.
+ return state_ % range;
+}
+
+// GTestIsInitialized() returns true iff the user has initialized
+// Google Test. Useful for catching the user mistake of not initializing
+// Google Test before calling RUN_ALL_TESTS().
+//
+// A user must call testing::InitGoogleTest() to initialize Google
+// Test. g_init_gtest_count is set to the number of times
+// InitGoogleTest() has been called. We don't protect this variable
+// under a mutex as it is only accessed in the main thread.
+GTEST_API_ int g_init_gtest_count = 0;
+static bool GTestIsInitialized() { return g_init_gtest_count != 0; }
+
+// Iterates over a vector of TestCases, keeping a running sum of the
+// results of calling a given int-returning method on each.
+// Returns the sum.
+static int SumOverTestCaseList(const std::vector<TestCase*>& case_list,
+ int (TestCase::*method)() const) {
+ int sum = 0;
+ for (size_t i = 0; i < case_list.size(); i++) {
+ sum += (case_list[i]->*method)();
+ }
+ return sum;
+}
+
+// Returns true iff the test case passed.
+static bool TestCasePassed(const TestCase* test_case) {
+ return test_case->should_run() && test_case->Passed();
+}
+
+// Returns true iff the test case failed.
+static bool TestCaseFailed(const TestCase* test_case) {
+ return test_case->should_run() && test_case->Failed();
+}
+
+// Returns true iff test_case contains at least one test that should
+// run.
+static bool ShouldRunTestCase(const TestCase* test_case) {
+ return test_case->should_run();
+}
+
+// AssertHelper constructor.
+AssertHelper::AssertHelper(TestPartResult::Type type,
+ const char* file,
+ int line,
+ const char* message)
+ : data_(new AssertHelperData(type, file, line, message)) {
+}
+
+AssertHelper::~AssertHelper() {
+ delete data_;
+}
+
+// Message assignment, for assertion streaming support.
+void AssertHelper::operator=(const Message& message) const {
+ UnitTest::GetInstance()->
+ AddTestPartResult(data_->type, data_->file, data_->line,
+ AppendUserMessage(data_->message, message),
+ UnitTest::GetInstance()->impl()
+ ->CurrentOsStackTraceExceptTop(1)
+ // Skips the stack frame for this function itself.
+ ); // NOLINT
+}
+
+// Mutex for linked pointers.
+GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex);
+
+// Application pathname gotten in InitGoogleTest.
+std::string g_executable_path;
+
+// Returns the current application's name, removing directory path if that
+// is present.
+FilePath GetCurrentExecutableName() {
+ FilePath result;
+
+#if GTEST_OS_WINDOWS
+ result.Set(FilePath(g_executable_path).RemoveExtension("exe"));
+#else
+ result.Set(FilePath(g_executable_path));
+#endif // GTEST_OS_WINDOWS
+
+ return result.RemoveDirectoryName();
+}
+
+// Functions for processing the gtest_output flag.
+
+// Returns the output format, or "" for normal printed output.
+std::string UnitTestOptions::GetOutputFormat() {
+ const char* const gtest_output_flag = GTEST_FLAG(output).c_str();
+ if (gtest_output_flag == NULL) return std::string("");
+
+ const char* const colon = strchr(gtest_output_flag, ':');
+ return (colon == NULL) ?
+ std::string(gtest_output_flag) :
+ std::string(gtest_output_flag, colon - gtest_output_flag);
+}
+
+// Returns the name of the requested output file, or the default if none
+// was explicitly specified.
+std::string UnitTestOptions::GetAbsolutePathToOutputFile() {
+ const char* const gtest_output_flag = GTEST_FLAG(output).c_str();
+ if (gtest_output_flag == NULL)
+ return "";
+
+ const char* const colon = strchr(gtest_output_flag, ':');
+ if (colon == NULL)
+ return internal::FilePath::ConcatPaths(
+ internal::FilePath(
+ UnitTest::GetInstance()->original_working_dir()),
+ internal::FilePath(kDefaultOutputFile)).string();
+
+ internal::FilePath output_name(colon + 1);
+ if (!output_name.IsAbsolutePath())
+ // TODO(wan@google.com): on Windows \some\path is not an absolute
+ // path (as its meaning depends on the current drive), yet the
+ // following logic for turning it into an absolute path is wrong.
+ // Fix it.
+ output_name = internal::FilePath::ConcatPaths(
+ internal::FilePath(UnitTest::GetInstance()->original_working_dir()),
+ internal::FilePath(colon + 1));
+
+ if (!output_name.IsDirectory())
+ return output_name.string();
+
+ internal::FilePath result(internal::FilePath::GenerateUniqueFileName(
+ output_name, internal::GetCurrentExecutableName(),
+ GetOutputFormat().c_str()));
+ return result.string();
+}
+
+// Returns true iff the wildcard pattern matches the string. The
+// first ':' or '\0' character in pattern marks the end of it.
+//
+// This recursive algorithm isn't very efficient, but is clear and
+// works well enough for matching test names, which are short.
+bool UnitTestOptions::PatternMatchesString(const char *pattern,
+ const char *str) {
+ switch (*pattern) {
+ case '\0':
+ case ':': // Either ':' or '\0' marks the end of the pattern.
+ return *str == '\0';
+ case '?': // Matches any single character.
+ return *str != '\0' && PatternMatchesString(pattern + 1, str + 1);
+ case '*': // Matches any string (possibly empty) of characters.
+ return (*str != '\0' && PatternMatchesString(pattern, str + 1)) ||
+ PatternMatchesString(pattern + 1, str);
+ default: // Non-special character. Matches itself.
+ return *pattern == *str &&
+ PatternMatchesString(pattern + 1, str + 1);
+ }
+}
+
+bool UnitTestOptions::MatchesFilter(
+ const std::string& name, const char* filter) {
+ const char *cur_pattern = filter;
+ for (;;) {
+ if (PatternMatchesString(cur_pattern, name.c_str())) {
+ return true;
+ }
+
+ // Finds the next pattern in the filter.
+ cur_pattern = strchr(cur_pattern, ':');
+
+ // Returns if no more pattern can be found.
+ if (cur_pattern == NULL) {
+ return false;
+ }
+
+ // Skips the pattern separater (the ':' character).
+ cur_pattern++;
+ }
+}
+
+// Returns true iff the user-specified filter matches the test case
+// name and the test name.
+bool UnitTestOptions::FilterMatchesTest(const std::string &test_case_name,
+ const std::string &test_name) {
+ const std::string& full_name = test_case_name + "." + test_name.c_str();
+
+ // Split --gtest_filter at '-', if there is one, to separate into
+ // positive filter and negative filter portions
+ const char* const p = GTEST_FLAG(filter).c_str();
+ const char* const dash = strchr(p, '-');
+ std::string positive;
+ std::string negative;
+ if (dash == NULL) {
+ positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter
+ negative = "";
+ } else {
+ positive = std::string(p, dash); // Everything up to the dash
+ negative = std::string(dash + 1); // Everything after the dash
+ if (positive.empty()) {
+ // Treat '-test1' as the same as '*-test1'
+ positive = kUniversalFilter;
+ }
+ }
+
+ // A filter is a colon-separated list of patterns. It matches a
+ // test if any pattern in it matches the test.
+ return (MatchesFilter(full_name, positive.c_str()) &&
+ !MatchesFilter(full_name, negative.c_str()));
+}
+
+#if GTEST_HAS_SEH
+// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the
+// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise.
+// This function is useful as an __except condition.
+int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) {
+ // Google Test should handle a SEH exception if:
+ // 1. the user wants it to, AND
+ // 2. this is not a breakpoint exception, AND
+ // 3. this is not a C++ exception (VC++ implements them via SEH,
+ // apparently).
+ //
+ // SEH exception code for C++ exceptions.
+ // (see http://support.microsoft.com/kb/185294 for more information).
+ const DWORD kCxxExceptionCode = 0xe06d7363;
+
+ bool should_handle = true;
+
+ if (!GTEST_FLAG(catch_exceptions))
+ should_handle = false;
+ else if (exception_code == EXCEPTION_BREAKPOINT)
+ should_handle = false;
+ else if (exception_code == kCxxExceptionCode)
+ should_handle = false;
+
+ return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
+}
+#endif // GTEST_HAS_SEH
+
+} // namespace internal
+
+// The c'tor sets this object as the test part result reporter used by
+// Google Test. The 'result' parameter specifies where to report the
+// results. Intercepts only failures from the current thread.
+ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter(
+ TestPartResultArray* result)
+ : intercept_mode_(INTERCEPT_ONLY_CURRENT_THREAD),
+ result_(result) {
+ Init();
+}
+
+// The c'tor sets this object as the test part result reporter used by
+// Google Test. The 'result' parameter specifies where to report the
+// results.
+ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter(
+ InterceptMode intercept_mode, TestPartResultArray* result)
+ : intercept_mode_(intercept_mode),
+ result_(result) {
+ Init();
+}
+
+void ScopedFakeTestPartResultReporter::Init() {
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ if (intercept_mode_ == INTERCEPT_ALL_THREADS) {
+ old_reporter_ = impl->GetGlobalTestPartResultReporter();
+ impl->SetGlobalTestPartResultReporter(this);
+ } else {
+ old_reporter_ = impl->GetTestPartResultReporterForCurrentThread();
+ impl->SetTestPartResultReporterForCurrentThread(this);
+ }
+}
+
+// The d'tor restores the test part result reporter used by Google Test
+// before.
+ScopedFakeTestPartResultReporter::~ScopedFakeTestPartResultReporter() {
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ if (intercept_mode_ == INTERCEPT_ALL_THREADS) {
+ impl->SetGlobalTestPartResultReporter(old_reporter_);
+ } else {
+ impl->SetTestPartResultReporterForCurrentThread(old_reporter_);
+ }
+}
+
+// Increments the test part result count and remembers the result.
+// This method is from the TestPartResultReporterInterface interface.
+void ScopedFakeTestPartResultReporter::ReportTestPartResult(
+ const TestPartResult& result) {
+ result_->Append(result);
+}
+
+namespace internal {
+
+// Returns the type ID of ::testing::Test. We should always call this
+// instead of GetTypeId< ::testing::Test>() to get the type ID of
+// testing::Test. This is to work around a suspected linker bug when
+// using Google Test as a framework on Mac OS X. The bug causes
+// GetTypeId< ::testing::Test>() to return different values depending
+// on whether the call is from the Google Test framework itself or
+// from user test code. GetTestTypeId() is guaranteed to always
+// return the same value, as it always calls GetTypeId<>() from the
+// gtest.cc, which is within the Google Test framework.
+TypeId GetTestTypeId() {
+ return GetTypeId<Test>();
+}
+
+// The value of GetTestTypeId() as seen from within the Google Test
+// library. This is solely for testing GetTestTypeId().
+extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId();
+
+// This predicate-formatter checks that 'results' contains a test part
+// failure of the given type and that the failure message contains the
+// given substring.
+AssertionResult HasOneFailure(const char* /* results_expr */,
+ const char* /* type_expr */,
+ const char* /* substr_expr */,
+ const TestPartResultArray& results,
+ TestPartResult::Type type,
+ const string& substr) {
+ const std::string expected(type == TestPartResult::kFatalFailure ?
+ "1 fatal failure" :
+ "1 non-fatal failure");
+ Message msg;
+ if (results.size() != 1) {
+ msg << "Expected: " << expected << "\n"
+ << " Actual: " << results.size() << " failures";
+ for (int i = 0; i < results.size(); i++) {
+ msg << "\n" << results.GetTestPartResult(i);
+ }
+ return AssertionFailure() << msg;
+ }
+
+ const TestPartResult& r = results.GetTestPartResult(0);
+ if (r.type() != type) {
+ return AssertionFailure() << "Expected: " << expected << "\n"
+ << " Actual:\n"
+ << r;
+ }
+
+ if (strstr(r.message(), substr.c_str()) == NULL) {
+ return AssertionFailure() << "Expected: " << expected << " containing \""
+ << substr << "\"\n"
+ << " Actual:\n"
+ << r;
+ }
+
+ return AssertionSuccess();
+}
+
+// The constructor of SingleFailureChecker remembers where to look up
+// test part results, what type of failure we expect, and what
+// substring the failure message should contain.
+SingleFailureChecker:: SingleFailureChecker(
+ const TestPartResultArray* results,
+ TestPartResult::Type type,
+ const string& substr)
+ : results_(results),
+ type_(type),
+ substr_(substr) {}
+
+// The destructor of SingleFailureChecker verifies that the given
+// TestPartResultArray contains exactly one failure that has the given
+// type and contains the given substring. If that's not the case, a
+// non-fatal failure will be generated.
+SingleFailureChecker::~SingleFailureChecker() {
+ EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_);
+}
+
+DefaultGlobalTestPartResultReporter::DefaultGlobalTestPartResultReporter(
+ UnitTestImpl* unit_test) : unit_test_(unit_test) {}
+
+void DefaultGlobalTestPartResultReporter::ReportTestPartResult(
+ const TestPartResult& result) {
+ unit_test_->current_test_result()->AddTestPartResult(result);
+ unit_test_->listeners()->repeater()->OnTestPartResult(result);
+}
+
+DefaultPerThreadTestPartResultReporter::DefaultPerThreadTestPartResultReporter(
+ UnitTestImpl* unit_test) : unit_test_(unit_test) {}
+
+void DefaultPerThreadTestPartResultReporter::ReportTestPartResult(
+ const TestPartResult& result) {
+ unit_test_->GetGlobalTestPartResultReporter()->ReportTestPartResult(result);
+}
+
+// Returns the global test part result reporter.
+TestPartResultReporterInterface*
+UnitTestImpl::GetGlobalTestPartResultReporter() {
+ internal::MutexLock lock(&global_test_part_result_reporter_mutex_);
+ return global_test_part_result_repoter_;
+}
+
+// Sets the global test part result reporter.
+void UnitTestImpl::SetGlobalTestPartResultReporter(
+ TestPartResultReporterInterface* reporter) {
+ internal::MutexLock lock(&global_test_part_result_reporter_mutex_);
+ global_test_part_result_repoter_ = reporter;
+}
+
+// Returns the test part result reporter for the current thread.
+TestPartResultReporterInterface*
+UnitTestImpl::GetTestPartResultReporterForCurrentThread() {
+ return per_thread_test_part_result_reporter_.get();
+}
+
+// Sets the test part result reporter for the current thread.
+void UnitTestImpl::SetTestPartResultReporterForCurrentThread(
+ TestPartResultReporterInterface* reporter) {
+ per_thread_test_part_result_reporter_.set(reporter);
+}
+
+// Gets the number of successful test cases.
+int UnitTestImpl::successful_test_case_count() const {
+ return CountIf(test_cases_, TestCasePassed);
+}
+
+// Gets the number of failed test cases.
+int UnitTestImpl::failed_test_case_count() const {
+ return CountIf(test_cases_, TestCaseFailed);
+}
+
+// Gets the number of all test cases.
+int UnitTestImpl::total_test_case_count() const {
+ return static_cast<int>(test_cases_.size());
+}
+
+// Gets the number of all test cases that contain at least one test
+// that should run.
+int UnitTestImpl::test_case_to_run_count() const {
+ return CountIf(test_cases_, ShouldRunTestCase);
+}
+
+// Gets the number of successful tests.
+int UnitTestImpl::successful_test_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::successful_test_count);
+}
+
+// Gets the number of failed tests.
+int UnitTestImpl::failed_test_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count);
+}
+
+// Gets the number of disabled tests that will be reported in the XML report.
+int UnitTestImpl::reportable_disabled_test_count() const {
+ return SumOverTestCaseList(test_cases_,
+ &TestCase::reportable_disabled_test_count);
+}
+
+// Gets the number of disabled tests.
+int UnitTestImpl::disabled_test_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count);
+}
+
+// Gets the number of tests to be printed in the XML report.
+int UnitTestImpl::reportable_test_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::reportable_test_count);
+}
+
+// Gets the number of all tests.
+int UnitTestImpl::total_test_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::total_test_count);
+}
+
+// Gets the number of tests that should run.
+int UnitTestImpl::test_to_run_count() const {
+ return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count);
+}
+
+// Returns the current OS stack trace as an std::string.
+//
+// The maximum number of stack frames to be included is specified by
+// the gtest_stack_trace_depth flag. The skip_count parameter
+// specifies the number of top frames to be skipped, which doesn't
+// count against the number of frames to be included.
+//
+// For example, if Foo() calls Bar(), which in turn calls
+// CurrentOsStackTraceExceptTop(1), Foo() will be included in the
+// trace but Bar() and CurrentOsStackTraceExceptTop() won't.
+std::string UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) {
+ (void)skip_count;
+ return "";
+}
+
+// Returns the current time in milliseconds.
+TimeInMillis GetTimeInMillis() {
+#if GTEST_OS_WINDOWS_MOBILE || defined(__BORLANDC__)
+ // Difference between 1970-01-01 and 1601-01-01 in milliseconds.
+ // http://analogous.blogspot.com/2005/04/epoch.html
+ const TimeInMillis kJavaEpochToWinFileTimeDelta =
+ static_cast<TimeInMillis>(116444736UL) * 100000UL;
+ const DWORD kTenthMicrosInMilliSecond = 10000;
+
+ SYSTEMTIME now_systime;
+ FILETIME now_filetime;
+ ULARGE_INTEGER now_int64;
+ // TODO(kenton@google.com): Shouldn't this just use
+ // GetSystemTimeAsFileTime()?
+ GetSystemTime(&now_systime);
+ if (SystemTimeToFileTime(&now_systime, &now_filetime)) {
+ now_int64.LowPart = now_filetime.dwLowDateTime;
+ now_int64.HighPart = now_filetime.dwHighDateTime;
+ now_int64.QuadPart = (now_int64.QuadPart / kTenthMicrosInMilliSecond) -
+ kJavaEpochToWinFileTimeDelta;
+ return now_int64.QuadPart;
+ }
+ return 0;
+#elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_
+ __timeb64 now;
+
+# ifdef _MSC_VER
+
+ // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996
+ // (deprecated function) there.
+ // TODO(kenton@google.com): Use GetTickCount()? Or use
+ // SystemTimeToFileTime()
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4996) // Temporarily disables warning 4996.
+ _ftime64(&now);
+# pragma warning(pop) // Restores the warning state.
+# else
+
+ _ftime64(&now);
+
+# endif // _MSC_VER
+
+ return static_cast<TimeInMillis>(now.time) * 1000 + now.millitm;
+#elif GTEST_HAS_GETTIMEOFDAY_
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ return static_cast<TimeInMillis>(now.tv_sec) * 1000 + now.tv_usec / 1000;
+#else
+# error "Don't know how to get the current time on your system."
+#endif
+}
+
+// Utilities
+
+// class String.
+
+#if GTEST_OS_WINDOWS_MOBILE
+// Creates a UTF-16 wide string from the given ANSI string, allocating
+// memory using new. The caller is responsible for deleting the return
+// value using delete[]. Returns the wide string, or NULL if the
+// input is NULL.
+LPCWSTR String::AnsiToUtf16(const char* ansi) {
+ if (!ansi) return NULL;
+ const int length = strlen(ansi);
+ const int unicode_length =
+ MultiByteToWideChar(CP_ACP, 0, ansi, length,
+ NULL, 0);
+ WCHAR* unicode = new WCHAR[unicode_length + 1];
+ MultiByteToWideChar(CP_ACP, 0, ansi, length,
+ unicode, unicode_length);
+ unicode[unicode_length] = 0;
+ return unicode;
+}
+
+// Creates an ANSI string from the given wide string, allocating
+// memory using new. The caller is responsible for deleting the return
+// value using delete[]. Returns the ANSI string, or NULL if the
+// input is NULL.
+const char* String::Utf16ToAnsi(LPCWSTR utf16_str) {
+ if (!utf16_str) return NULL;
+ const int ansi_length =
+ WideCharToMultiByte(CP_ACP, 0, utf16_str, -1,
+ NULL, 0, NULL, NULL);
+ char* ansi = new char[ansi_length + 1];
+ WideCharToMultiByte(CP_ACP, 0, utf16_str, -1,
+ ansi, ansi_length, NULL, NULL);
+ ansi[ansi_length] = 0;
+ return ansi;
+}
+
+#endif // GTEST_OS_WINDOWS_MOBILE
+
+// Compares two C strings. Returns true iff they have the same content.
+//
+// Unlike strcmp(), this function can handle NULL argument(s). A NULL
+// C string is considered different to any non-NULL C string,
+// including the empty string.
+bool String::CStringEquals(const char * lhs, const char * rhs) {
+ if ( lhs == NULL ) return rhs == NULL;
+
+ if ( rhs == NULL ) return false;
+
+ return strcmp(lhs, rhs) == 0;
+}
+
+#if GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING
+
+// Converts an array of wide chars to a narrow string using the UTF-8
+// encoding, and streams the result to the given Message object.
+static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length,
+ Message* msg) {
+ for (size_t i = 0; i != length; ) { // NOLINT
+ if (wstr[i] != L'\0') {
+ *msg << WideStringToUtf8(wstr + i, static_cast<int>(length - i));
+ while (i != length && wstr[i] != L'\0')
+ i++;
+ } else {
+ *msg << '\0';
+ i++;
+ }
+ }
+}
+
+#endif // GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING
+
+} // namespace internal
+
+// Constructs an empty Message.
+// We allocate the stringstream separately because otherwise each use of
+// ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's
+// stack frame leading to huge stack frames in some cases; gcc does not reuse
+// the stack space.
+Message::Message() : ss_(new ::std::stringstream) {
+ // By default, we want there to be enough precision when printing
+ // a double to a Message.
+ *ss_ << std::setprecision(std::numeric_limits<double>::digits10 + 2);
+}
+
+// These two overloads allow streaming a wide C string to a Message
+// using the UTF-8 encoding.
+Message& Message::operator <<(const wchar_t* wide_c_str) {
+ return *this << internal::String::ShowWideCString(wide_c_str);
+}
+Message& Message::operator <<(wchar_t* wide_c_str) {
+ return *this << internal::String::ShowWideCString(wide_c_str);
+}
+
+#if GTEST_HAS_STD_WSTRING
+// Converts the given wide string to a narrow string using the UTF-8
+// encoding, and streams the result to this Message object.
+Message& Message::operator <<(const ::std::wstring& wstr) {
+ internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this);
+ return *this;
+}
+#endif // GTEST_HAS_STD_WSTRING
+
+#if GTEST_HAS_GLOBAL_WSTRING
+// Converts the given wide string to a narrow string using the UTF-8
+// encoding, and streams the result to this Message object.
+Message& Message::operator <<(const ::wstring& wstr) {
+ internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this);
+ return *this;
+}
+#endif // GTEST_HAS_GLOBAL_WSTRING
+
+// Gets the text streamed to this object so far as an std::string.
+// Each '\0' character in the buffer is replaced with "\\0".
+std::string Message::GetString() const {
+ return internal::StringStreamToString(ss_.get());
+}
+
+// AssertionResult constructors.
+// Used in EXPECT_TRUE/FALSE(assertion_result).
+AssertionResult::AssertionResult(const AssertionResult& other)
+ : success_(other.success_),
+ message_(other.message_.get() != NULL ?
+ new ::std::string(*other.message_) :
+ static_cast< ::std::string*>(NULL)) {
+}
+
+// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE.
+AssertionResult AssertionResult::operator!() const {
+ AssertionResult negation(!success_);
+ if (message_.get() != NULL)
+ negation << *message_;
+ return negation;
+}
+
+// Makes a successful assertion result.
+AssertionResult AssertionSuccess() {
+ return AssertionResult(true);
+}
+
+// Makes a failed assertion result.
+AssertionResult AssertionFailure() {
+ return AssertionResult(false);
+}
+
+// Makes a failed assertion result with the given failure message.
+// Deprecated; use AssertionFailure() << message.
+AssertionResult AssertionFailure(const Message& message) {
+ return AssertionFailure() << message;
+}
+
+namespace internal {
+
+// Constructs and returns the message for an equality assertion
+// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure.
+//
+// The first four parameters are the expressions used in the assertion
+// and their values, as strings. For example, for ASSERT_EQ(foo, bar)
+// where foo is 5 and bar is 6, we have:
+//
+// expected_expression: "foo"
+// actual_expression: "bar"
+// expected_value: "5"
+// actual_value: "6"
+//
+// The ignoring_case parameter is true iff the assertion is a
+// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will
+// be inserted into the message.
+AssertionResult EqFailure(const char* expected_expression,
+ const char* actual_expression,
+ const std::string& expected_value,
+ const std::string& actual_value,
+ bool ignoring_case) {
+ Message msg;
+ msg << "Value of: " << actual_expression;
+ if (actual_value != actual_expression) {
+ msg << "\n Actual: " << actual_value;
+ }
+
+ msg << "\nExpected: " << expected_expression;
+ if (ignoring_case) {
+ msg << " (ignoring case)";
+ }
+ if (expected_value != expected_expression) {
+ msg << "\nWhich is: " << expected_value;
+ }
+
+ return AssertionFailure() << msg;
+}
+
+// Constructs a failure message for Boolean assertions such as EXPECT_TRUE.
+std::string GetBoolAssertionFailureMessage(
+ const AssertionResult& assertion_result,
+ const char* expression_text,
+ const char* actual_predicate_value,
+ const char* expected_predicate_value) {
+ const char* actual_message = assertion_result.message();
+ Message msg;
+ msg << "Value of: " << expression_text
+ << "\n Actual: " << actual_predicate_value;
+ if (actual_message[0] != '\0')
+ msg << " (" << actual_message << ")";
+ msg << "\nExpected: " << expected_predicate_value;
+ return msg.GetString();
+}
+
+// Helper function for implementing ASSERT_NEAR.
+AssertionResult DoubleNearPredFormat(const char* expr1,
+ const char* expr2,
+ const char* abs_error_expr,
+ double val1,
+ double val2,
+ double abs_error) {
+ const double diff = fabs(val1 - val2);
+ if (diff <= abs_error) return AssertionSuccess();
+
+ // TODO(wan): do not print the value of an expression if it's
+ // already a literal.
+ return AssertionFailure()
+ << "The difference between " << expr1 << " and " << expr2
+ << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n"
+ << expr1 << " evaluates to " << val1 << ",\n"
+ << expr2 << " evaluates to " << val2 << ", and\n"
+ << abs_error_expr << " evaluates to " << abs_error << ".";
+}
+
+
+// Helper template for implementing FloatLE() and DoubleLE().
+template <typename RawType>
+AssertionResult FloatingPointLE(const char* expr1,
+ const char* expr2,
+ RawType val1,
+ RawType val2) {
+ // Returns success if val1 is less than val2,
+ if (val1 < val2) {
+ return AssertionSuccess();
+ }
+
+ // or if val1 is almost equal to val2.
+ const FloatingPoint<RawType> lhs(val1), rhs(val2);
+ if (lhs.AlmostEquals(rhs)) {
+ return AssertionSuccess();
+ }
+
+ // Note that the above two checks will both fail if either val1 or
+ // val2 is NaN, as the IEEE floating-point standard requires that
+ // any predicate involving a NaN must return false.
+
+ ::std::stringstream val1_ss;
+ val1_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
+ << val1;
+
+ ::std::stringstream val2_ss;
+ val2_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
+ << val2;
+
+ return AssertionFailure()
+ << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n"
+ << " Actual: " << StringStreamToString(&val1_ss) << " vs "
+ << StringStreamToString(&val2_ss);
+}
+
+} // namespace internal
+
+// Asserts that val1 is less than, or almost equal to, val2. Fails
+// otherwise. In particular, it fails if either val1 or val2 is NaN.
+AssertionResult FloatLE(const char* expr1, const char* expr2,
+ float val1, float val2) {
+ return internal::FloatingPointLE<float>(expr1, expr2, val1, val2);
+}
+
+// Asserts that val1 is less than, or almost equal to, val2. Fails
+// otherwise. In particular, it fails if either val1 or val2 is NaN.
+AssertionResult DoubleLE(const char* expr1, const char* expr2,
+ double val1, double val2) {
+ return internal::FloatingPointLE<double>(expr1, expr2, val1, val2);
+}
+
+namespace internal {
+
+// The helper function for {ASSERT|EXPECT}_EQ with int or enum
+// arguments.
+AssertionResult CmpHelperEQ(const char* expected_expression,
+ const char* actual_expression,
+ BiggestInt expected,
+ BiggestInt actual) {
+ if (expected == actual) {
+ return AssertionSuccess();
+ }
+
+ return EqFailure(expected_expression,
+ actual_expression,
+ FormatForComparisonFailureMessage(expected, actual),
+ FormatForComparisonFailureMessage(actual, expected),
+ false);
+}
+
+// A macro for implementing the helper functions needed to implement
+// ASSERT_?? and EXPECT_?? with integer or enum arguments. It is here
+// just to avoid copy-and-paste of similar code.
+#define GTEST_IMPL_CMP_HELPER_(op_name, op)\
+AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \
+ BiggestInt val1, BiggestInt val2) {\
+ if (val1 op val2) {\
+ return AssertionSuccess();\
+ } else {\
+ return AssertionFailure() \
+ << "Expected: (" << expr1 << ") " #op " (" << expr2\
+ << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\
+ << " vs " << FormatForComparisonFailureMessage(val2, val1);\
+ }\
+}
+
+// Implements the helper function for {ASSERT|EXPECT}_NE with int or
+// enum arguments.
+GTEST_IMPL_CMP_HELPER_(NE, !=)
+// Implements the helper function for {ASSERT|EXPECT}_LE with int or
+// enum arguments.
+GTEST_IMPL_CMP_HELPER_(LE, <=)
+// Implements the helper function for {ASSERT|EXPECT}_LT with int or
+// enum arguments.
+GTEST_IMPL_CMP_HELPER_(LT, < )
+// Implements the helper function for {ASSERT|EXPECT}_GE with int or
+// enum arguments.
+GTEST_IMPL_CMP_HELPER_(GE, >=)
+// Implements the helper function for {ASSERT|EXPECT}_GT with int or
+// enum arguments.
+GTEST_IMPL_CMP_HELPER_(GT, > )
+
+#undef GTEST_IMPL_CMP_HELPER_
+
+// The helper function for {ASSERT|EXPECT}_STREQ.
+AssertionResult CmpHelperSTREQ(const char* expected_expression,
+ const char* actual_expression,
+ const char* expected,
+ const char* actual) {
+ if (String::CStringEquals(expected, actual)) {
+ return AssertionSuccess();
+ }
+
+ return EqFailure(expected_expression,
+ actual_expression,
+ PrintToString(expected),
+ PrintToString(actual),
+ false);
+}
+
+// The helper function for {ASSERT|EXPECT}_STRCASEEQ.
+AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression,
+ const char* actual_expression,
+ const char* expected,
+ const char* actual) {
+ if (String::CaseInsensitiveCStringEquals(expected, actual)) {
+ return AssertionSuccess();
+ }
+
+ return EqFailure(expected_expression,
+ actual_expression,
+ PrintToString(expected),
+ PrintToString(actual),
+ true);
+}
+
+// The helper function for {ASSERT|EXPECT}_STRNE.
+AssertionResult CmpHelperSTRNE(const char* s1_expression,
+ const char* s2_expression,
+ const char* s1,
+ const char* s2) {
+ if (!String::CStringEquals(s1, s2)) {
+ return AssertionSuccess();
+ } else {
+ return AssertionFailure() << "Expected: (" << s1_expression << ") != ("
+ << s2_expression << "), actual: \""
+ << s1 << "\" vs \"" << s2 << "\"";
+ }
+}
+
+// The helper function for {ASSERT|EXPECT}_STRCASENE.
+AssertionResult CmpHelperSTRCASENE(const char* s1_expression,
+ const char* s2_expression,
+ const char* s1,
+ const char* s2) {
+ if (!String::CaseInsensitiveCStringEquals(s1, s2)) {
+ return AssertionSuccess();
+ } else {
+ return AssertionFailure()
+ << "Expected: (" << s1_expression << ") != ("
+ << s2_expression << ") (ignoring case), actual: \""
+ << s1 << "\" vs \"" << s2 << "\"";
+ }
+}
+
+} // namespace internal
+
+namespace {
+
+// Helper functions for implementing IsSubString() and IsNotSubstring().
+
+// This group of overloaded functions return true iff needle is a
+// substring of haystack. NULL is considered a substring of itself
+// only.
+
+bool IsSubstringPred(const char* needle, const char* haystack) {
+ if (needle == NULL || haystack == NULL)
+ return needle == haystack;
+
+ return strstr(haystack, needle) != NULL;
+}
+
+bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) {
+ if (needle == NULL || haystack == NULL)
+ return needle == haystack;
+
+ return wcsstr(haystack, needle) != NULL;
+}
+
+// StringType here can be either ::std::string or ::std::wstring.
+template <typename StringType>
+bool IsSubstringPred(const StringType& needle,
+ const StringType& haystack) {
+ return haystack.find(needle) != StringType::npos;
+}
+
+// This function implements either IsSubstring() or IsNotSubstring(),
+// depending on the value of the expected_to_be_substring parameter.
+// StringType here can be const char*, const wchar_t*, ::std::string,
+// or ::std::wstring.
+template <typename StringType>
+AssertionResult IsSubstringImpl(
+ bool expected_to_be_substring,
+ const char* needle_expr, const char* haystack_expr,
+ const StringType& needle, const StringType& haystack) {
+ if (IsSubstringPred(needle, haystack) == expected_to_be_substring)
+ return AssertionSuccess();
+
+ const bool is_wide_string = sizeof(needle[0]) > 1;
+ const char* const begin_string_quote = is_wide_string ? "L\"" : "\"";
+ return AssertionFailure()
+ << "Value of: " << needle_expr << "\n"
+ << " Actual: " << begin_string_quote << needle << "\"\n"
+ << "Expected: " << (expected_to_be_substring ? "" : "not ")
+ << "a substring of " << haystack_expr << "\n"
+ << "Which is: " << begin_string_quote << haystack << "\"";
+}
+
+} // namespace
+
+// IsSubstring() and IsNotSubstring() check whether needle is a
+// substring of haystack (NULL is considered a substring of itself
+// only), and return an appropriate error message when they fail.
+
+AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const char* needle, const char* haystack) {
+ return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const wchar_t* needle, const wchar_t* haystack) {
+ return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const char* needle, const char* haystack) {
+ return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const wchar_t* needle, const wchar_t* haystack) {
+ return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::string& needle, const ::std::string& haystack) {
+ return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::string& needle, const ::std::string& haystack) {
+ return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack);
+}
+
+#if GTEST_HAS_STD_WSTRING
+AssertionResult IsSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::wstring& needle, const ::std::wstring& haystack) {
+ return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack);
+}
+
+AssertionResult IsNotSubstring(
+ const char* needle_expr, const char* haystack_expr,
+ const ::std::wstring& needle, const ::std::wstring& haystack) {
+ return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack);
+}
+#endif // GTEST_HAS_STD_WSTRING
+
+namespace internal {
+
+#if GTEST_OS_WINDOWS
+
+namespace {
+
+// Helper function for IsHRESULT{SuccessFailure} predicates
+AssertionResult HRESULTFailureHelper(const char* expr,
+ const char* expected,
+ long hr) { // NOLINT
+# if GTEST_OS_WINDOWS_MOBILE
+
+ // Windows CE doesn't support FormatMessage.
+ const char error_text[] = "";
+
+# else
+
+ // Looks up the human-readable system message for the HRESULT code
+ // and since we're not passing any params to FormatMessage, we don't
+ // want inserts expanded.
+ const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS;
+ const DWORD kBufSize = 4096;
+ // Gets the system's human readable message string for this HRESULT.
+ char error_text[kBufSize] = { '\0' };
+ DWORD message_length = ::FormatMessageA(kFlags,
+ 0, // no source, we're asking system
+ hr, // the error
+ 0, // no line width restrictions
+ error_text, // output buffer
+ kBufSize, // buf size
+ NULL); // no arguments for inserts
+ // Trims tailing white space (FormatMessage leaves a trailing CR-LF)
+ for (; message_length && IsSpace(error_text[message_length - 1]);
+ --message_length) {
+ error_text[message_length - 1] = '\0';
+ }
+
+# endif // GTEST_OS_WINDOWS_MOBILE
+
+ const std::string error_hex("0x" + String::FormatHexInt(hr));
+ return ::testing::AssertionFailure()
+ << "Expected: " << expr << " " << expected << ".\n"
+ << " Actual: " << error_hex << " " << error_text << "\n";
+}
+
+} // namespace
+
+AssertionResult IsHRESULTSuccess(const char* expr, long hr) { // NOLINT
+ if (SUCCEEDED(hr)) {
+ return AssertionSuccess();
+ }
+ return HRESULTFailureHelper(expr, "succeeds", hr);
+}
+
+AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT
+ if (FAILED(hr)) {
+ return AssertionSuccess();
+ }
+ return HRESULTFailureHelper(expr, "fails", hr);
+}
+
+#endif // GTEST_OS_WINDOWS
+
+// Utility functions for encoding Unicode text (wide strings) in
+// UTF-8.
+
+// A Unicode code-point can have upto 21 bits, and is encoded in UTF-8
+// like this:
+//
+// Code-point length Encoding
+// 0 - 7 bits 0xxxxxxx
+// 8 - 11 bits 110xxxxx 10xxxxxx
+// 12 - 16 bits 1110xxxx 10xxxxxx 10xxxxxx
+// 17 - 21 bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+
+// The maximum code-point a one-byte UTF-8 sequence can represent.
+const UInt32 kMaxCodePoint1 = (static_cast<UInt32>(1) << 7) - 1;
+
+// The maximum code-point a two-byte UTF-8 sequence can represent.
+const UInt32 kMaxCodePoint2 = (static_cast<UInt32>(1) << (5 + 6)) - 1;
+
+// The maximum code-point a three-byte UTF-8 sequence can represent.
+const UInt32 kMaxCodePoint3 = (static_cast<UInt32>(1) << (4 + 2*6)) - 1;
+
+// The maximum code-point a four-byte UTF-8 sequence can represent.
+const UInt32 kMaxCodePoint4 = (static_cast<UInt32>(1) << (3 + 3*6)) - 1;
+
+// Chops off the n lowest bits from a bit pattern. Returns the n
+// lowest bits. As a side effect, the original bit pattern will be
+// shifted to the right by n bits.
+inline UInt32 ChopLowBits(UInt32* bits, int n) {
+ const UInt32 low_bits = *bits & ((static_cast<UInt32>(1) << n) - 1);
+ *bits >>= n;
+ return low_bits;
+}
+
+// Converts a Unicode code point to a narrow string in UTF-8 encoding.
+// code_point parameter is of type UInt32 because wchar_t may not be
+// wide enough to contain a code point.
+// If the code_point is not a valid Unicode code point
+// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted
+// to "(Invalid Unicode 0xXXXXXXXX)".
+std::string CodePointToUtf8(UInt32 code_point) {
+ if (code_point > kMaxCodePoint4) {
+ return "(Invalid Unicode 0x" + String::FormatHexInt(code_point) + ")";
+ }
+
+ char str[5]; // Big enough for the largest valid code point.
+ if (code_point <= kMaxCodePoint1) {
+ str[1] = '\0';
+ str[0] = static_cast<char>(code_point); // 0xxxxxxx
+ } else if (code_point <= kMaxCodePoint2) {
+ str[2] = '\0';
+ str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[0] = static_cast<char>(0xC0 | code_point); // 110xxxxx
+ } else if (code_point <= kMaxCodePoint3) {
+ str[3] = '\0';
+ str[2] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[0] = static_cast<char>(0xE0 | code_point); // 1110xxxx
+ } else { // code_point <= kMaxCodePoint4
+ str[4] = '\0';
+ str[3] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[2] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx
+ str[0] = static_cast<char>(0xF0 | code_point); // 11110xxx
+ }
+ return str;
+}
+
+// The following two functions only make sense if the the system
+// uses UTF-16 for wide string encoding. All supported systems
+// with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16.
+
+// Determines if the arguments constitute UTF-16 surrogate pair
+// and thus should be combined into a single Unicode code point
+// using CreateCodePointFromUtf16SurrogatePair.
+inline bool IsUtf16SurrogatePair(wchar_t first, wchar_t second) {
+ return sizeof(wchar_t) == 2 &&
+ (first & 0xFC00) == 0xD800 && (second & 0xFC00) == 0xDC00;
+}
+
+// Creates a Unicode code point from UTF16 surrogate pair.
+inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first,
+ wchar_t second) {
+ const UInt32 mask = (1 << 10) - 1;
+ return (sizeof(wchar_t) == 2) ?
+ (((first & mask) << 10) | (second & mask)) + 0x10000 :
+ // This function should not be called when the condition is
+ // false, but we provide a sensible default in case it is.
+ static_cast<UInt32>(first);
+}
+
+// Converts a wide string to a narrow string in UTF-8 encoding.
+// The wide string is assumed to have the following encoding:
+// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS)
+// UTF-32 if sizeof(wchar_t) == 4 (on Linux)
+// Parameter str points to a null-terminated wide string.
+// Parameter num_chars may additionally limit the number
+// of wchar_t characters processed. -1 is used when the entire string
+// should be processed.
+// If the string contains code points that are not valid Unicode code points
+// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output
+// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding
+// and contains invalid UTF-16 surrogate pairs, values in those pairs
+// will be encoded as individual Unicode characters from Basic Normal Plane.
+std::string WideStringToUtf8(const wchar_t* str, int num_chars) {
+ if (num_chars == -1)
+ num_chars = static_cast<int>(wcslen(str));
+
+ ::std::stringstream stream;
+ for (int i = 0; i < num_chars; ++i) {
+ UInt32 unicode_code_point;
+
+ if (str[i] == L'\0') {
+ break;
+ } else if (i + 1 < num_chars && IsUtf16SurrogatePair(str[i], str[i + 1])) {
+ unicode_code_point = CreateCodePointFromUtf16SurrogatePair(str[i],
+ str[i + 1]);
+ i++;
+ } else {
+ unicode_code_point = static_cast<UInt32>(str[i]);
+ }
+
+ stream << CodePointToUtf8(unicode_code_point);
+ }
+ return StringStreamToString(&stream);
+}
+
+// Converts a wide C string to an std::string using the UTF-8 encoding.
+// NULL will be converted to "(null)".
+std::string String::ShowWideCString(const wchar_t * wide_c_str) {
+ if (wide_c_str == NULL) return "(null)";
+
+ return internal::WideStringToUtf8(wide_c_str, -1);
+}
+
+// Compares two wide C strings. Returns true iff they have the same
+// content.
+//
+// Unlike wcscmp(), this function can handle NULL argument(s). A NULL
+// C string is considered different to any non-NULL C string,
+// including the empty string.
+bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) {
+ if (lhs == NULL) return rhs == NULL;
+
+ if (rhs == NULL) return false;
+
+ return wcscmp(lhs, rhs) == 0;
+}
+
+// Helper function for *_STREQ on wide strings.
+AssertionResult CmpHelperSTREQ(const char* expected_expression,
+ const char* actual_expression,
+ const wchar_t* expected,
+ const wchar_t* actual) {
+ if (String::WideCStringEquals(expected, actual)) {
+ return AssertionSuccess();
+ }
+
+ return EqFailure(expected_expression,
+ actual_expression,
+ PrintToString(expected),
+ PrintToString(actual),
+ false);
+}
+
+// Helper function for *_STRNE on wide strings.
+AssertionResult CmpHelperSTRNE(const char* s1_expression,
+ const char* s2_expression,
+ const wchar_t* s1,
+ const wchar_t* s2) {
+ if (!String::WideCStringEquals(s1, s2)) {
+ return AssertionSuccess();
+ }
+
+ return AssertionFailure() << "Expected: (" << s1_expression << ") != ("
+ << s2_expression << "), actual: "
+ << PrintToString(s1)
+ << " vs " << PrintToString(s2);
+}
+
+// Compares two C strings, ignoring case. Returns true iff they have
+// the same content.
+//
+// Unlike strcasecmp(), this function can handle NULL argument(s). A
+// NULL C string is considered different to any non-NULL C string,
+// including the empty string.
+bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) {
+ if (lhs == NULL)
+ return rhs == NULL;
+ if (rhs == NULL)
+ return false;
+ return posix::StrCaseCmp(lhs, rhs) == 0;
+}
+
+ // Compares two wide C strings, ignoring case. Returns true iff they
+ // have the same content.
+ //
+ // Unlike wcscasecmp(), this function can handle NULL argument(s).
+ // A NULL C string is considered different to any non-NULL wide C string,
+ // including the empty string.
+ // NB: The implementations on different platforms slightly differ.
+ // On windows, this method uses _wcsicmp which compares according to LC_CTYPE
+ // environment variable. On GNU platform this method uses wcscasecmp
+ // which compares according to LC_CTYPE category of the current locale.
+ // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the
+ // current locale.
+bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs,
+ const wchar_t* rhs) {
+ if (lhs == NULL) return rhs == NULL;
+
+ if (rhs == NULL) return false;
+
+#if GTEST_OS_WINDOWS
+ return _wcsicmp(lhs, rhs) == 0;
+#elif GTEST_OS_LINUX && !GTEST_OS_LINUX_ANDROID
+ return wcscasecmp(lhs, rhs) == 0;
+#else
+ // Android, Mac OS X and Cygwin don't define wcscasecmp.
+ // Other unknown OSes may not define it either.
+ wint_t left, right;
+ do {
+ left = towlower(*lhs++);
+ right = towlower(*rhs++);
+ } while (left && left == right);
+ return left == right;
+#endif // OS selector
+}
+
+// Returns true iff str ends with the given suffix, ignoring case.
+// Any string is considered to end with an empty suffix.
+bool String::EndsWithCaseInsensitive(
+ const std::string& str, const std::string& suffix) {
+ const size_t str_len = str.length();
+ const size_t suffix_len = suffix.length();
+ return (str_len >= suffix_len) &&
+ CaseInsensitiveCStringEquals(str.c_str() + str_len - suffix_len,
+ suffix.c_str());
+}
+
+// Formats an int value as "%02d".
+std::string String::FormatIntWidth2(int value) {
+ std::stringstream ss;
+ ss << std::setfill('0') << std::setw(2) << value;
+ return ss.str();
+}
+
+// Formats an int value as "%X".
+std::string String::FormatHexInt(int value) {
+ std::stringstream ss;
+ ss << std::hex << std::uppercase << value;
+ return ss.str();
+}
+
+// Formats a byte as "%02X".
+std::string String::FormatByte(unsigned char value) {
+ std::stringstream ss;
+ ss << std::setfill('0') << std::setw(2) << std::hex << std::uppercase
+ << static_cast<unsigned int>(value);
+ return ss.str();
+}
+
+// Converts the buffer in a stringstream to an std::string, converting NUL
+// bytes to "\\0" along the way.
+std::string StringStreamToString(::std::stringstream* ss) {
+ const ::std::string& str = ss->str();
+ const char* const start = str.c_str();
+ const char* const end = start + str.length();
+
+ std::string result;
+ result.reserve(2 * (end - start));
+ for (const char* ch = start; ch != end; ++ch) {
+ if (*ch == '\0') {
+ result += "\\0"; // Replaces NUL with "\\0";
+ } else {
+ result += *ch;
+ }
+ }
+
+ return result;
+}
+
+// Appends the user-supplied message to the Google-Test-generated message.
+std::string AppendUserMessage(const std::string& gtest_msg,
+ const Message& user_msg) {
+ // Appends the user message if it's non-empty.
+ const std::string user_msg_string = user_msg.GetString();
+ if (user_msg_string.empty()) {
+ return gtest_msg;
+ }
+
+ return gtest_msg + "\n" + user_msg_string;
+}
+
+} // namespace internal
+
+// class TestResult
+
+// Creates an empty TestResult.
+TestResult::TestResult()
+ : death_test_count_(0),
+ elapsed_time_(0) {
+}
+
+// D'tor.
+TestResult::~TestResult() {
+}
+
+// Returns the i-th test part result among all the results. i can
+// range from 0 to total_part_count() - 1. If i is not in that range,
+// aborts the program.
+const TestPartResult& TestResult::GetTestPartResult(int i) const {
+ if (i < 0 || i >= total_part_count())
+ internal::posix::Abort();
+ return test_part_results_.at(i);
+}
+
+// Returns the i-th test property. i can range from 0 to
+// test_property_count() - 1. If i is not in that range, aborts the
+// program.
+const TestProperty& TestResult::GetTestProperty(int i) const {
+ if (i < 0 || i >= test_property_count())
+ internal::posix::Abort();
+ return test_properties_.at(i);
+}
+
+// Clears the test part results.
+void TestResult::ClearTestPartResults() {
+ test_part_results_.clear();
+}
+
+// Adds a test part result to the list.
+void TestResult::AddTestPartResult(const TestPartResult& test_part_result) {
+ test_part_results_.push_back(test_part_result);
+}
+
+// Adds a test property to the list. If a property with the same key as the
+// supplied property is already represented, the value of this test_property
+// replaces the old value for that key.
+void TestResult::RecordProperty(const std::string& xml_element,
+ const TestProperty& test_property) {
+ if (!ValidateTestProperty(xml_element, test_property)) {
+ return;
+ }
+ internal::MutexLock lock(&test_properites_mutex_);
+ const std::vector<TestProperty>::iterator property_with_matching_key =
+ std::find_if(test_properties_.begin(), test_properties_.end(),
+ internal::TestPropertyKeyIs(test_property.key()));
+ if (property_with_matching_key == test_properties_.end()) {
+ test_properties_.push_back(test_property);
+ return;
+ }
+ property_with_matching_key->SetValue(test_property.value());
+}
+
+// The list of reserved attributes used in the <testsuites> element of XML
+// output.
+static const char* const kReservedTestSuitesAttributes[] = {
+ "disabled",
+ "errors",
+ "failures",
+ "name",
+ "random_seed",
+ "tests",
+ "time",
+ "timestamp"
+};
+
+// The list of reserved attributes used in the <testsuite> element of XML
+// output.
+static const char* const kReservedTestSuiteAttributes[] = {
+ "disabled",
+ "errors",
+ "failures",
+ "name",
+ "tests",
+ "time"
+};
+
+// The list of reserved attributes used in the <testcase> element of XML output.
+static const char* const kReservedTestCaseAttributes[] = {
+ "classname",
+ "name",
+ "status",
+ "time",
+ "type_param",
+ "value_param"
+};
+
+template <int kSize>
+std::vector<std::string> ArrayAsVector(const char* const (&array)[kSize]) {
+ return std::vector<std::string>(array, array + kSize);
+}
+
+static std::vector<std::string> GetReservedAttributesForElement(
+ const std::string& xml_element) {
+ if (xml_element == "testsuites") {
+ return ArrayAsVector(kReservedTestSuitesAttributes);
+ } else if (xml_element == "testsuite") {
+ return ArrayAsVector(kReservedTestSuiteAttributes);
+ } else if (xml_element == "testcase") {
+ return ArrayAsVector(kReservedTestCaseAttributes);
+ } else {
+ GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element;
+ }
+ // This code is unreachable but some compilers may not realizes that.
+ return std::vector<std::string>();
+}
+
+static std::string FormatWordList(const std::vector<std::string>& words) {
+ Message word_list;
+ for (size_t i = 0; i < words.size(); ++i) {
+ if (i > 0 && words.size() > 2) {
+ word_list << ", ";
+ }
+ if (i == words.size() - 1) {
+ word_list << "and ";
+ }
+ word_list << "'" << words[i] << "'";
+ }
+ return word_list.GetString();
+}
+
+bool ValidateTestPropertyName(const std::string& property_name,
+ const std::vector<std::string>& reserved_names) {
+ if (std::find(reserved_names.begin(), reserved_names.end(), property_name) !=
+ reserved_names.end()) {
+ ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name
+ << " (" << FormatWordList(reserved_names)
+ << " are reserved by " << GTEST_NAME_ << ")";
+ return false;
+ }
+ return true;
+}
+
+// Adds a failure if the key is a reserved attribute of the element named
+// xml_element. Returns true if the property is valid.
+bool TestResult::ValidateTestProperty(const std::string& xml_element,
+ const TestProperty& test_property) {
+ return ValidateTestPropertyName(test_property.key(),
+ GetReservedAttributesForElement(xml_element));
+}
+
+// Clears the object.
+void TestResult::Clear() {
+ test_part_results_.clear();
+ test_properties_.clear();
+ death_test_count_ = 0;
+ elapsed_time_ = 0;
+}
+
+// Returns true iff the test failed.
+bool TestResult::Failed() const {
+ for (int i = 0; i < total_part_count(); ++i) {
+ if (GetTestPartResult(i).failed())
+ return true;
+ }
+ return false;
+}
+
+// Returns true iff the test part fatally failed.
+static bool TestPartFatallyFailed(const TestPartResult& result) {
+ return result.fatally_failed();
+}
+
+// Returns true iff the test fatally failed.
+bool TestResult::HasFatalFailure() const {
+ return CountIf(test_part_results_, TestPartFatallyFailed) > 0;
+}
+
+// Returns true iff the test part non-fatally failed.
+static bool TestPartNonfatallyFailed(const TestPartResult& result) {
+ return result.nonfatally_failed();
+}
+
+// Returns true iff the test has a non-fatal failure.
+bool TestResult::HasNonfatalFailure() const {
+ return CountIf(test_part_results_, TestPartNonfatallyFailed) > 0;
+}
+
+// Gets the number of all test parts. This is the sum of the number
+// of successful test parts and the number of failed test parts.
+int TestResult::total_part_count() const {
+ return static_cast<int>(test_part_results_.size());
+}
+
+// Returns the number of the test properties.
+int TestResult::test_property_count() const {
+ return static_cast<int>(test_properties_.size());
+}
+
+// class Test
+
+// Creates a Test object.
+
+// The c'tor saves the values of all Google Test flags.
+Test::Test()
+ : gtest_flag_saver_(new internal::GTestFlagSaver) {
+}
+
+// The d'tor restores the values of all Google Test flags.
+Test::~Test() {
+ delete gtest_flag_saver_;
+}
+
+// Sets up the test fixture.
+//
+// A sub-class may override this.
+void Test::SetUp() {
+}
+
+// Tears down the test fixture.
+//
+// A sub-class may override this.
+void Test::TearDown() {
+}
+
+// Allows user supplied key value pairs to be recorded for later output.
+void Test::RecordProperty(const std::string& key, const std::string& value) {
+ UnitTest::GetInstance()->RecordProperty(key, value);
+}
+
+// Allows user supplied key value pairs to be recorded for later output.
+void Test::RecordProperty(const std::string& key, int value) {
+ Message value_message;
+ value_message << value;
+ RecordProperty(key, value_message.GetString().c_str());
+}
+
+namespace internal {
+
+void ReportFailureInUnknownLocation(TestPartResult::Type result_type,
+ const std::string& message) {
+ // This function is a friend of UnitTest and as such has access to
+ // AddTestPartResult.
+ UnitTest::GetInstance()->AddTestPartResult(
+ result_type,
+ NULL, // No info about the source file where the exception occurred.
+ -1, // We have no info on which line caused the exception.
+ message,
+ ""); // No stack trace, either.
+}
+
+} // namespace internal
+
+// Google Test requires all tests in the same test case to use the same test
+// fixture class. This function checks if the current test has the
+// same fixture class as the first test in the current test case. If
+// yes, it returns true; otherwise it generates a Google Test failure and
+// returns false.
+bool Test::HasSameFixtureClass() {
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ const TestCase* const test_case = impl->current_test_case();
+
+ // Info about the first test in the current test case.
+ const TestInfo* const first_test_info = test_case->test_info_list()[0];
+ const internal::TypeId first_fixture_id = first_test_info->fixture_class_id_;
+ const char* const first_test_name = first_test_info->name();
+
+ // Info about the current test.
+ const TestInfo* const this_test_info = impl->current_test_info();
+ const internal::TypeId this_fixture_id = this_test_info->fixture_class_id_;
+ const char* const this_test_name = this_test_info->name();
+
+ if (this_fixture_id != first_fixture_id) {
+ // Is the first test defined using TEST?
+ const bool first_is_TEST = first_fixture_id == internal::GetTestTypeId();
+ // Is this test defined using TEST?
+ const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId();
+
+ if (first_is_TEST || this_is_TEST) {
+ // The user mixed TEST and TEST_F in this test case - we'll tell
+ // him/her how to fix it.
+
+ // Gets the name of the TEST and the name of the TEST_F. Note
+ // that first_is_TEST and this_is_TEST cannot both be true, as
+ // the fixture IDs are different for the two tests.
+ const char* const TEST_name =
+ first_is_TEST ? first_test_name : this_test_name;
+ const char* const TEST_F_name =
+ first_is_TEST ? this_test_name : first_test_name;
+
+ ADD_FAILURE()
+ << "All tests in the same test case must use the same test fixture\n"
+ << "class, so mixing TEST_F and TEST in the same test case is\n"
+ << "illegal. In test case " << this_test_info->test_case_name()
+ << ",\n"
+ << "test " << TEST_F_name << " is defined using TEST_F but\n"
+ << "test " << TEST_name << " is defined using TEST. You probably\n"
+ << "want to change the TEST to TEST_F or move it to another test\n"
+ << "case.";
+ } else {
+ // The user defined two fixture classes with the same name in
+ // two namespaces - we'll tell him/her how to fix it.
+ ADD_FAILURE()
+ << "All tests in the same test case must use the same test fixture\n"
+ << "class. However, in test case "
+ << this_test_info->test_case_name() << ",\n"
+ << "you defined test " << first_test_name
+ << " and test " << this_test_name << "\n"
+ << "using two different test fixture classes. This can happen if\n"
+ << "the two classes are from different namespaces or translation\n"
+ << "units and have the same name. You should probably rename one\n"
+ << "of the classes to put the tests into different test cases.";
+ }
+ return false;
+ }
+
+ return true;
+}
+
+#if GTEST_HAS_SEH
+
+// Adds an "exception thrown" fatal failure to the current test. This
+// function returns its result via an output parameter pointer because VC++
+// prohibits creation of objects with destructors on stack in functions
+// using __try (see error C2712).
+static std::string* FormatSehExceptionMessage(DWORD exception_code,
+ const char* location) {
+ Message message;
+ message << "SEH exception with code 0x" << std::setbase(16) <<
+ exception_code << std::setbase(10) << " thrown in " << location << ".";
+
+ return new std::string(message.GetString());
+}
+
+#endif // GTEST_HAS_SEH
+
+namespace internal {
+
+#if GTEST_HAS_EXCEPTIONS
+
+// Adds an "exception thrown" fatal failure to the current test.
+static std::string FormatCxxExceptionMessage(const char* description,
+ const char* location) {
+ Message message;
+ if (description != NULL) {
+ message << "C++ exception with description \"" << description << "\"";
+ } else {
+ message << "Unknown C++ exception";
+ }
+ message << " thrown in " << location << ".";
+
+ return message.GetString();
+}
+
+static std::string PrintTestPartResultToString(
+ const TestPartResult& test_part_result);
+
+GoogleTestFailureException::GoogleTestFailureException(
+ const TestPartResult& failure)
+ : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {}
+
+#endif // GTEST_HAS_EXCEPTIONS
+
+// We put these helper functions in the internal namespace as IBM's xlC
+// compiler rejects the code if they were declared static.
+
+// Runs the given method and handles SEH exceptions it throws, when
+// SEH is supported; returns the 0-value for type Result in case of an
+// SEH exception. (Microsoft compilers cannot handle SEH and C++
+// exceptions in the same function. Therefore, we provide a separate
+// wrapper function for handling SEH exceptions.)
+template <class T, typename Result>
+Result HandleSehExceptionsInMethodIfSupported(
+ T* object, Result (T::*method)(), const char* location) {
+#if GTEST_HAS_SEH
+ __try {
+ return (object->*method)();
+ } __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT
+ GetExceptionCode())) {
+ // We create the exception message on the heap because VC++ prohibits
+ // creation of objects with destructors on stack in functions using __try
+ // (see error C2712).
+ std::string* exception_message = FormatSehExceptionMessage(
+ GetExceptionCode(), location);
+ internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure,
+ *exception_message);
+ delete exception_message;
+ return static_cast<Result>(0);
+ }
+#else
+ (void)location;
+ return (object->*method)();
+#endif // GTEST_HAS_SEH
+}
+
+// Runs the given method and catches and reports C++ and/or SEH-style
+// exceptions, if they are supported; returns the 0-value for type
+// Result in case of an SEH exception.
+template <class T, typename Result>
+Result HandleExceptionsInMethodIfSupported(
+ T* object, Result (T::*method)(), const char* location) {
+ // NOTE: The user code can affect the way in which Google Test handles
+ // exceptions by setting GTEST_FLAG(catch_exceptions), but only before
+ // RUN_ALL_TESTS() starts. It is technically possible to check the flag
+ // after the exception is caught and either report or re-throw the
+ // exception based on the flag's value:
+ //
+ // try {
+ // // Perform the test method.
+ // } catch (...) {
+ // if (GTEST_FLAG(catch_exceptions))
+ // // Report the exception as failure.
+ // else
+ // throw; // Re-throws the original exception.
+ // }
+ //
+ // However, the purpose of this flag is to allow the program to drop into
+ // the debugger when the exception is thrown. On most platforms, once the
+ // control enters the catch block, the exception origin information is
+ // lost and the debugger will stop the program at the point of the
+ // re-throw in this function -- instead of at the point of the original
+ // throw statement in the code under test. For this reason, we perform
+ // the check early, sacrificing the ability to affect Google Test's
+ // exception handling in the method where the exception is thrown.
+ if (internal::GetUnitTestImpl()->catch_exceptions()) {
+#if GTEST_HAS_EXCEPTIONS
+ try {
+ return HandleSehExceptionsInMethodIfSupported(object, method, location);
+ } catch (const internal::GoogleTestFailureException&) { // NOLINT
+ // This exception type can only be thrown by a failed Google
+ // Test assertion with the intention of letting another testing
+ // framework catch it. Therefore we just re-throw it.
+ throw;
+ } catch (const std::exception& e) { // NOLINT
+ internal::ReportFailureInUnknownLocation(
+ TestPartResult::kFatalFailure,
+ FormatCxxExceptionMessage(e.what(), location));
+ } catch (...) { // NOLINT
+ internal::ReportFailureInUnknownLocation(
+ TestPartResult::kFatalFailure,
+ FormatCxxExceptionMessage(NULL, location));
+ }
+ return static_cast<Result>(0);
+#else
+ return HandleSehExceptionsInMethodIfSupported(object, method, location);
+#endif // GTEST_HAS_EXCEPTIONS
+ } else {
+ return (object->*method)();
+ }
+}
+
+} // namespace internal
+
+// Runs the test and updates the test result.
+void Test::Run() {
+ if (!HasSameFixtureClass()) return;
+
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()");
+ // We will run the test only if SetUp() was successful.
+ if (!HasFatalFailure()) {
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(
+ this, &Test::TestBody, "the test body");
+ }
+
+ // However, we want to clean up as much as possible. Hence we will
+ // always call TearDown(), even if SetUp() or the test body has
+ // failed.
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(
+ this, &Test::TearDown, "TearDown()");
+}
+
+// Returns true iff the current test has a fatal failure.
+bool Test::HasFatalFailure() {
+ return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure();
+}
+
+// Returns true iff the current test has a non-fatal failure.
+bool Test::HasNonfatalFailure() {
+ return internal::GetUnitTestImpl()->current_test_result()->
+ HasNonfatalFailure();
+}
+
+// class TestInfo
+
+// Constructs a TestInfo object. It assumes ownership of the test factory
+// object.
+TestInfo::TestInfo(const std::string& a_test_case_name,
+ const std::string& a_name,
+ const char* a_type_param,
+ const char* a_value_param,
+ internal::TypeId fixture_class_id,
+ internal::TestFactoryBase* factory)
+ : test_case_name_(a_test_case_name),
+ name_(a_name),
+ type_param_(a_type_param ? new std::string(a_type_param) : NULL),
+ value_param_(a_value_param ? new std::string(a_value_param) : NULL),
+ fixture_class_id_(fixture_class_id),
+ should_run_(false),
+ is_disabled_(false),
+ matches_filter_(false),
+ factory_(factory),
+ result_() {}
+
+// Destructs a TestInfo object.
+TestInfo::~TestInfo() { delete factory_; }
+
+namespace internal {
+
+// Creates a new TestInfo object and registers it with Google Test;
+// returns the created object.
+//
+// Arguments:
+//
+// test_case_name: name of the test case
+// name: name of the test
+// type_param: the name of the test's type parameter, or NULL if
+// this is not a typed or a type-parameterized test.
+// value_param: text representation of the test's value parameter,
+// or NULL if this is not a value-parameterized test.
+// fixture_class_id: ID of the test fixture class
+// set_up_tc: pointer to the function that sets up the test case
+// tear_down_tc: pointer to the function that tears down the test case
+// factory: pointer to the factory that creates a test object.
+// The newly created TestInfo instance will assume
+// ownership of the factory object.
+TestInfo* MakeAndRegisterTestInfo(
+ const char* test_case_name,
+ const char* name,
+ const char* type_param,
+ const char* value_param,
+ TypeId fixture_class_id,
+ SetUpTestCaseFunc set_up_tc,
+ TearDownTestCaseFunc tear_down_tc,
+ TestFactoryBase* factory) {
+ TestInfo* const test_info =
+ new TestInfo(test_case_name, name, type_param, value_param,
+ fixture_class_id, factory);
+ GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);
+ return test_info;
+}
+
+#if GTEST_HAS_PARAM_TEST
+void ReportInvalidTestCaseType(const char* test_case_name,
+ const char* file, int line) {
+ Message errors;
+ errors
+ << "Attempted redefinition of test case " << test_case_name << ".\n"
+ << "All tests in the same test case must use the same test fixture\n"
+ << "class. However, in test case " << test_case_name << ", you tried\n"
+ << "to define a test using a fixture class different from the one\n"
+ << "used earlier. This can happen if the two fixture classes are\n"
+ << "from different namespaces and have the same name. You should\n"
+ << "probably rename one of the classes to put the tests into different\n"
+ << "test cases.";
+
+ fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(),
+ errors.GetString().c_str());
+}
+#endif // GTEST_HAS_PARAM_TEST
+
+} // namespace internal
+
+namespace {
+
+// A predicate that checks the test name of a TestInfo against a known
+// value.
+//
+// This is used for implementation of the TestCase class only. We put
+// it in the anonymous namespace to prevent polluting the outer
+// namespace.
+//
+// TestNameIs is copyable.
+class TestNameIs {
+ public:
+ // Constructor.
+ //
+ // TestNameIs has NO default constructor.
+ explicit TestNameIs(const char* name)
+ : name_(name) {}
+
+ // Returns true iff the test name of test_info matches name_.
+ bool operator()(const TestInfo * test_info) const {
+ return test_info && test_info->name() == name_;
+ }
+
+ private:
+ std::string name_;
+};
+
+} // namespace
+
+namespace internal {
+
+// This method expands all parameterized tests registered with macros TEST_P
+// and INSTANTIATE_TEST_CASE_P into regular tests and registers those.
+// This will be done just once during the program runtime.
+void UnitTestImpl::RegisterParameterizedTests() {
+#if GTEST_HAS_PARAM_TEST
+ if (!parameterized_tests_registered_) {
+ parameterized_test_registry_.RegisterTests();
+ parameterized_tests_registered_ = true;
+ }
+#endif
+}
+
+} // namespace internal
+
+// Creates the test object, runs it, records its result, and then
+// deletes it.
+void TestInfo::Run() {
+ if (!should_run_) return;
+
+ // Tells UnitTest where to store test result.
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ impl->set_current_test_info(this);
+
+ TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater();
+
+ // Notifies the unit test event listeners that a test is about to start.
+ repeater->OnTestStart(*this);
+
+ const TimeInMillis start = internal::GetTimeInMillis();
+
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+
+ // Creates the test object.
+ Test* const test = internal::HandleExceptionsInMethodIfSupported(
+ factory_, &internal::TestFactoryBase::CreateTest,
+ "the test fixture's constructor");
+
+ // Runs the test only if the test object was created and its
+ // constructor didn't generate a fatal failure.
+ if ((test != NULL) && !Test::HasFatalFailure()) {
+ // This doesn't throw as all user code that can throw are wrapped into
+ // exception handling code.
+ test->Run();
+ }
+
+ // Deletes the test object.
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(
+ test, &Test::DeleteSelf_, "the test fixture's destructor");
+
+ result_.set_elapsed_time(internal::GetTimeInMillis() - start);
+
+ // Notifies the unit test event listener that a test has just finished.
+ repeater->OnTestEnd(*this);
+
+ // Tells UnitTest to stop associating assertion results to this
+ // test.
+ impl->set_current_test_info(NULL);
+}
+
+// class TestCase
+
+// Gets the number of successful tests in this test case.
+int TestCase::successful_test_count() const {
+ return CountIf(test_info_list_, TestPassed);
+}
+
+// Gets the number of failed tests in this test case.
+int TestCase::failed_test_count() const {
+ return CountIf(test_info_list_, TestFailed);
+}
+
+// Gets the number of disabled tests that will be reported in the XML report.
+int TestCase::reportable_disabled_test_count() const {
+ return CountIf(test_info_list_, TestReportableDisabled);
+}
+
+// Gets the number of disabled tests in this test case.
+int TestCase::disabled_test_count() const {
+ return CountIf(test_info_list_, TestDisabled);
+}
+
+// Gets the number of tests to be printed in the XML report.
+int TestCase::reportable_test_count() const {
+ return CountIf(test_info_list_, TestReportable);
+}
+
+// Get the number of tests in this test case that should run.
+int TestCase::test_to_run_count() const {
+ return CountIf(test_info_list_, ShouldRunTest);
+}
+
+// Gets the number of all tests.
+int TestCase::total_test_count() const {
+ return static_cast<int>(test_info_list_.size());
+}
+
+// Creates a TestCase with the given name.
+//
+// Arguments:
+//
+// name: name of the test case
+// a_type_param: the name of the test case's type parameter, or NULL if
+// this is not a typed or a type-parameterized test case.
+// set_up_tc: pointer to the function that sets up the test case
+// tear_down_tc: pointer to the function that tears down the test case
+TestCase::TestCase(const char* a_name, const char* a_type_param,
+ Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc)
+ : name_(a_name),
+ type_param_(a_type_param ? new std::string(a_type_param) : NULL),
+ set_up_tc_(set_up_tc),
+ tear_down_tc_(tear_down_tc),
+ should_run_(false),
+ elapsed_time_(0) {
+}
+
+// Destructor of TestCase.
+TestCase::~TestCase() {
+ // Deletes every Test in the collection.
+ ForEach(test_info_list_, internal::Delete<TestInfo>);
+}
+
+// Returns the i-th test among all the tests. i can range from 0 to
+// total_test_count() - 1. If i is not in that range, returns NULL.
+const TestInfo* TestCase::GetTestInfo(int i) const {
+ const int index = GetElementOr(test_indices_, i, -1);
+ return index < 0 ? NULL : test_info_list_[index];
+}
+
+// Returns the i-th test among all the tests. i can range from 0 to
+// total_test_count() - 1. If i is not in that range, returns NULL.
+TestInfo* TestCase::GetMutableTestInfo(int i) {
+ const int index = GetElementOr(test_indices_, i, -1);
+ return index < 0 ? NULL : test_info_list_[index];
+}
+
+// Adds a test to this test case. Will delete the test upon
+// destruction of the TestCase object.
+void TestCase::AddTestInfo(TestInfo * test_info) {
+ test_info_list_.push_back(test_info);
+ test_indices_.push_back(static_cast<int>(test_indices_.size()));
+}
+
+// Runs every test in this TestCase.
+void TestCase::Run() {
+ if (!should_run_) return;
+
+ internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+ impl->set_current_test_case(this);
+
+ TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater();
+
+ repeater->OnTestCaseStart(*this);
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(
+ this, &TestCase::RunSetUpTestCase, "SetUpTestCase()");
+
+ const internal::TimeInMillis start = internal::GetTimeInMillis();
+ for (int i = 0; i < total_test_count(); i++) {
+ GetMutableTestInfo(i)->Run();
+ }
+ elapsed_time_ = internal::GetTimeInMillis() - start;
+
+ impl->os_stack_trace_getter()->UponLeavingGTest();
+ internal::HandleExceptionsInMethodIfSupported(
+ this, &TestCase::RunTearDownTestCase, "TearDownTestCase()");
+
+ repeater->OnTestCaseEnd(*this);
+ impl->set_current_test_case(NULL);
+}
+
+// Clears the results of all tests in this test case.
+void TestCase::ClearResult() {
+ ad_hoc_test_result_.Clear();
+ ForEach(test_info_list_, TestInfo::ClearTestResult);
+}
+
+// Shuffles the tests in this test case.
+void TestCase::ShuffleTests(internal::Random* random) {
+ Shuffle(random, &test_indices_);
+}
+
+// Restores the test order to before the first shuffle.
+void TestCase::UnshuffleTests() {
+ for (size_t i = 0; i < test_indices_.size(); i++) {
+ test_indices_[i] = static_cast<int>(i);
+ }
+}
+
+// Formats a countable noun. Depending on its quantity, either the
+// singular form or the plural form is used. e.g.
+//
+// FormatCountableNoun(1, "formula", "formuli") returns "1 formula".
+// FormatCountableNoun(5, "book", "books") returns "5 books".
+static std::string FormatCountableNoun(int count,
+ const char * singular_form,
+ const char * plural_form) {
+ return internal::StreamableToString(count) + " " +
+ (count == 1 ? singular_form : plural_form);
+}
+
+// Formats the count of tests.
+static std::string FormatTestCount(int test_count) {
+ return FormatCountableNoun(test_count, "test", "tests");
+}
+
+// Formats the count of test cases.
+static std::string FormatTestCaseCount(int test_case_count) {
+ return FormatCountableNoun(test_case_count, "test case", "test cases");
+}
+
+// Converts a TestPartResult::Type enum to human-friendly string
+// representation. Both kNonFatalFailure and kFatalFailure are translated
+// to "Failure", as the user usually doesn't care about the difference
+// between the two when viewing the test result.
+static const char * TestPartResultTypeToString(TestPartResult::Type type) {
+ switch (type) {
+ case TestPartResult::kSuccess:
+ return "Success";
+
+ case TestPartResult::kNonFatalFailure:
+ case TestPartResult::kFatalFailure:
+#ifdef _MSC_VER
+ return "error: ";
+#else
+ return "Failure\n";
+#endif
+ default:
+ return "Unknown result type";
+ }
+}
+
+namespace internal {
+
+// Prints a TestPartResult to an std::string.
+static std::string PrintTestPartResultToString(
+ const TestPartResult& test_part_result) {
+ return (Message()
+ << internal::FormatFileLocation(test_part_result.file_name(),
+ test_part_result.line_number())
+ << " " << TestPartResultTypeToString(test_part_result.type())
+ << test_part_result.message()).GetString();
+}
+
+// Prints a TestPartResult.
+static void PrintTestPartResult(const TestPartResult& test_part_result) {
+ const std::string& result =
+ PrintTestPartResultToString(test_part_result);
+ printf("%s\n", result.c_str());
+ fflush(stdout);
+ // If the test program runs in Visual Studio or a debugger, the
+ // following statements add the test part result message to the Output
+ // window such that the user can double-click on it to jump to the
+ // corresponding source code location; otherwise they do nothing.
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+ // We don't call OutputDebugString*() on Windows Mobile, as printing
+ // to stdout is done by OutputDebugString() there already - we don't
+ // want the same message printed twice.
+ ::OutputDebugStringA(result.c_str());
+ ::OutputDebugStringA("\n");
+#endif
+}
+
+// class PrettyUnitTestResultPrinter
+
+enum GTestColor {
+ COLOR_DEFAULT,
+ COLOR_RED,
+ COLOR_GREEN,
+ COLOR_YELLOW
+};
+
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+
+// Returns the character attribute for the given color.
+WORD GetColorAttribute(GTestColor color) {
+ switch (color) {
+ case COLOR_RED: return FOREGROUND_RED;
+ case COLOR_GREEN: return FOREGROUND_GREEN;
+ case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN;
+ default: return 0;
+ }
+}
+
+#else
+
+// Returns the ANSI color code for the given color. COLOR_DEFAULT is
+// an invalid input.
+const char* GetAnsiColorCode(GTestColor color) {
+ switch (color) {
+ case COLOR_RED: return "1";
+ case COLOR_GREEN: return "2";
+ case COLOR_YELLOW: return "3";
+ default: return NULL;
+ };
+}
+
+#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+
+// Returns true iff Google Test should use colors in the output.
+bool ShouldUseColor(bool stdout_is_tty) {
+ const char* const gtest_color = GTEST_FLAG(color).c_str();
+
+ if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) {
+#if GTEST_OS_WINDOWS
+ // On Windows the TERM variable is usually not set, but the
+ // console there does support colors.
+ return stdout_is_tty;
+#else
+ // On non-Windows platforms, we rely on the TERM variable.
+ const char* const term = posix::GetEnv("TERM");
+ const bool term_supports_color =
+ String::CStringEquals(term, "xterm") ||
+ String::CStringEquals(term, "xterm-color") ||
+ String::CStringEquals(term, "xterm-256color") ||
+ String::CStringEquals(term, "screen") ||
+ String::CStringEquals(term, "screen-256color") ||
+ String::CStringEquals(term, "linux") ||
+ String::CStringEquals(term, "cygwin");
+ return stdout_is_tty && term_supports_color;
+#endif // GTEST_OS_WINDOWS
+ }
+
+ return String::CaseInsensitiveCStringEquals(gtest_color, "yes") ||
+ String::CaseInsensitiveCStringEquals(gtest_color, "true") ||
+ String::CaseInsensitiveCStringEquals(gtest_color, "t") ||
+ String::CStringEquals(gtest_color, "1");
+ // We take "yes", "true", "t", and "1" as meaning "yes". If the
+ // value is neither one of these nor "auto", we treat it as "no" to
+ // be conservative.
+}
+
+// Helpers for printing colored strings to stdout. Note that on Windows, we
+// cannot simply emit special characters and have the terminal change colors.
+// This routine must actually emit the characters rather than return a string
+// that would be colored when printed, as can be done on Linux.
+void ColoredPrintf(GTestColor color, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+
+#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS || GTEST_OS_IOS
+ const bool use_color = false;
+#else
+ static const bool in_color_mode =
+ ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0);
+ const bool use_color = in_color_mode && (color != COLOR_DEFAULT);
+#endif // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS
+ // The '!= 0' comparison is necessary to satisfy MSVC 7.1.
+
+ if (!use_color) {
+ vprintf(fmt, args);
+ va_end(args);
+ return;
+ }
+
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+ const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ // Gets the current text color.
+ CONSOLE_SCREEN_BUFFER_INFO buffer_info;
+ GetConsoleScreenBufferInfo(stdout_handle, &buffer_info);
+ const WORD old_color_attrs = buffer_info.wAttributes;
+
+ // We need to flush the stream buffers into the console before each
+ // SetConsoleTextAttribute call lest it affect the text that is already
+ // printed but has not yet reached the console.
+ fflush(stdout);
+ SetConsoleTextAttribute(stdout_handle,
+ GetColorAttribute(color) | FOREGROUND_INTENSITY);
+ vprintf(fmt, args);
+
+ fflush(stdout);
+ // Restores the text color.
+ SetConsoleTextAttribute(stdout_handle, old_color_attrs);
+#else
+ printf("\033[0;3%sm", GetAnsiColorCode(color));
+ vprintf(fmt, args);
+ printf("\033[m"); // Resets the terminal to default.
+#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+ va_end(args);
+}
+
+// Text printed in Google Test's text output and --gunit_list_tests
+// output to label the type parameter and value parameter for a test.
+static const char kTypeParamLabel[] = "TypeParam";
+static const char kValueParamLabel[] = "GetParam()";
+
+void PrintFullTestCommentIfPresent(const TestInfo& test_info) {
+ const char* const type_param = test_info.type_param();
+ const char* const value_param = test_info.value_param();
+
+ if (type_param != NULL || value_param != NULL) {
+ printf(", where ");
+ if (type_param != NULL) {
+ printf("%s = %s", kTypeParamLabel, type_param);
+ if (value_param != NULL)
+ printf(" and ");
+ }
+ if (value_param != NULL) {
+ printf("%s = %s", kValueParamLabel, value_param);
+ }
+ }
+}
+
+// This class implements the TestEventListener interface.
+//
+// Class PrettyUnitTestResultPrinter is copyable.
+class PrettyUnitTestResultPrinter : public TestEventListener {
+ public:
+ PrettyUnitTestResultPrinter() {}
+ static void PrintTestName(const char * test_case, const char * test) {
+ printf("%s.%s", test_case, test);
+ }
+
+ // The following methods override what's in the TestEventListener class.
+ virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {}
+ virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration);
+ virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test);
+ virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {}
+ virtual void OnTestCaseStart(const TestCase& test_case);
+ virtual void OnTestStart(const TestInfo& test_info);
+ virtual void OnTestPartResult(const TestPartResult& result);
+ virtual void OnTestEnd(const TestInfo& test_info);
+ virtual void OnTestCaseEnd(const TestCase& test_case);
+ virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test);
+ virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {}
+ virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);
+ virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {}
+
+ private:
+ static void PrintFailedTests(const UnitTest& unit_test);
+};
+
+ // Fired before each iteration of tests starts.
+void PrettyUnitTestResultPrinter::OnTestIterationStart(
+ const UnitTest& unit_test, int iteration) {
+ if (GTEST_FLAG(repeat) != 1)
+ printf("\nRepeating all tests (iteration %d) . . .\n\n", iteration + 1);
+
+ const char* const filter = GTEST_FLAG(filter).c_str();
+
+ // Prints the filter if it's not *. This reminds the user that some
+ // tests may be skipped.
+ if (!String::CStringEquals(filter, kUniversalFilter)) {
+ ColoredPrintf(COLOR_YELLOW,
+ "Note: %s filter = %s\n", GTEST_NAME_, filter);
+ }
+
+ if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) {
+ const Int32 shard_index = Int32FromEnvOrDie(kTestShardIndex, -1);
+ ColoredPrintf(COLOR_YELLOW,
+ "Note: This is test shard %d of %s.\n",
+ static_cast<int>(shard_index) + 1,
+ internal::posix::GetEnv(kTestTotalShards));
+ }
+
+ if (GTEST_FLAG(shuffle)) {
+ ColoredPrintf(COLOR_YELLOW,
+ "Note: Randomizing tests' orders with a seed of %d .\n",
+ unit_test.random_seed());
+ }
+
+ ColoredPrintf(COLOR_GREEN, "[==========] ");
+ printf("Running %s from %s.\n",
+ FormatTestCount(unit_test.test_to_run_count()).c_str(),
+ FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str());
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart(
+ const UnitTest& /*unit_test*/) {
+ ColoredPrintf(COLOR_GREEN, "[----------] ");
+ printf("Global test environment set-up.\n");
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) {
+ const std::string counts =
+ FormatCountableNoun(test_case.test_to_run_count(), "test", "tests");
+ ColoredPrintf(COLOR_GREEN, "[----------] ");
+ printf("%s from %s", counts.c_str(), test_case.name());
+ if (test_case.type_param() == NULL) {
+ printf("\n");
+ } else {
+ printf(", where %s = %s\n", kTypeParamLabel, test_case.type_param());
+ }
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) {
+ ColoredPrintf(COLOR_GREEN, "[ RUN ] ");
+ PrintTestName(test_info.test_case_name(), test_info.name());
+ printf("\n");
+ fflush(stdout);
+}
+
+// Called after an assertion failure.
+void PrettyUnitTestResultPrinter::OnTestPartResult(
+ const TestPartResult& result) {
+ // If the test part succeeded, we don't need to do anything.
+ if (result.type() == TestPartResult::kSuccess)
+ return;
+
+ // Print failure message from the assertion (e.g. expected this and got that).
+ PrintTestPartResult(result);
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) {
+ if (test_info.result()->Passed()) {
+ ColoredPrintf(COLOR_GREEN, "[ OK ] ");
+ } else {
+ ColoredPrintf(COLOR_RED, "[ FAILED ] ");
+ }
+ PrintTestName(test_info.test_case_name(), test_info.name());
+ if (test_info.result()->Failed())
+ PrintFullTestCommentIfPresent(test_info);
+
+ if (GTEST_FLAG(print_time)) {
+ printf(" (%s ms)\n", internal::StreamableToString(
+ test_info.result()->elapsed_time()).c_str());
+ } else {
+ printf("\n");
+ }
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) {
+ if (!GTEST_FLAG(print_time)) return;
+
+ const std::string counts =
+ FormatCountableNoun(test_case.test_to_run_count(), "test", "tests");
+ ColoredPrintf(COLOR_GREEN, "[----------] ");
+ printf("%s from %s (%s ms total)\n\n",
+ counts.c_str(), test_case.name(),
+ internal::StreamableToString(test_case.elapsed_time()).c_str());
+ fflush(stdout);
+}
+
+void PrettyUnitTestResultPrinter::OnEnvironmentsTearDownStart(
+ const UnitTest& /*unit_test*/) {
+ ColoredPrintf(COLOR_GREEN, "[----------] ");
+ printf("Global test environment tear-down\n");
+ fflush(stdout);
+}
+
+// Internal helper for printing the list of failed tests.
+void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) {
+ const int failed_test_count = unit_test.failed_test_count();
+ if (failed_test_count == 0) {
+ return;
+ }
+
+ for (int i = 0; i < unit_test.total_test_case_count(); ++i) {
+ const TestCase& test_case = *unit_test.GetTestCase(i);
+ if (!test_case.should_run() || (test_case.failed_test_count() == 0)) {
+ continue;
+ }
+ for (int j = 0; j < test_case.total_test_count(); ++j) {
+ const TestInfo& test_info = *test_case.GetTestInfo(j);
+ if (!test_info.should_run() || test_info.result()->Passed()) {
+ continue;
+ }
+ ColoredPrintf(COLOR_RED, "[ FAILED ] ");
+ printf("%s.%s", test_case.name(), test_info.name());
+ PrintFullTestCommentIfPresent(test_info);
+ printf("\n");
+ }
+ }
+}
+
+void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
+ int /*iteration*/) {
+ ColoredPrintf(COLOR_GREEN, "[==========] ");
+ printf("%s from %s ran.",
+ FormatTestCount(unit_test.test_to_run_count()).c_str(),
+ FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str());
+ if (GTEST_FLAG(print_time)) {
+ printf(" (%s ms total)",
+ internal::StreamableToString(unit_test.elapsed_time()).c_str());
+ }
+ printf("\n");
+ ColoredPrintf(COLOR_GREEN, "[ PASSED ] ");
+ printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str());
+
+ int num_failures = unit_test.failed_test_count();
+ if (!unit_test.Passed()) {
+ const int failed_test_count = unit_test.failed_test_count();
+ ColoredPrintf(COLOR_RED, "[ FAILED ] ");
+ printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str());
+ PrintFailedTests(unit_test);
+ printf("\n%2d FAILED %s\n", num_failures,
+ num_failures == 1 ? "TEST" : "TESTS");
+ }
+
+ int num_disabled = unit_test.reportable_disabled_test_count();
+ if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) {
+ if (!num_failures) {
+ printf("\n"); // Add a spacer if no FAILURE banner is displayed.
+ }
+ ColoredPrintf(COLOR_YELLOW,
+ " YOU HAVE %d DISABLED %s\n\n",
+ num_disabled,
+ num_disabled == 1 ? "TEST" : "TESTS");
+ }
+ // Ensure that Google Test output is printed before, e.g., heapchecker output.
+ fflush(stdout);
+}
+
+// End PrettyUnitTestResultPrinter
+
+// class TestEventRepeater
+//
+// This class forwards events to other event listeners.
+class TestEventRepeater : public TestEventListener {
+ public:
+ TestEventRepeater() : forwarding_enabled_(true) {}
+ virtual ~TestEventRepeater();
+ void Append(TestEventListener *listener);
+ TestEventListener* Release(TestEventListener* listener);
+
+ // Controls whether events will be forwarded to listeners_. Set to false
+ // in death test child processes.
+ bool forwarding_enabled() const { return forwarding_enabled_; }
+ void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; }
+
+ virtual void OnTestProgramStart(const UnitTest& unit_test);
+ virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration);
+ virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test);
+ virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test);
+ virtual void OnTestCaseStart(const TestCase& test_case);
+ virtual void OnTestStart(const TestInfo& test_info);
+ virtual void OnTestPartResult(const TestPartResult& result);
+ virtual void OnTestEnd(const TestInfo& test_info);
+ virtual void OnTestCaseEnd(const TestCase& test_case);
+ virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test);
+ virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test);
+ virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);
+ virtual void OnTestProgramEnd(const UnitTest& unit_test);
+
+ private:
+ // Controls whether events will be forwarded to listeners_. Set to false
+ // in death test child processes.
+ bool forwarding_enabled_;
+ // The list of listeners that receive events.
+ std::vector<TestEventListener*> listeners_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventRepeater);
+};
+
+TestEventRepeater::~TestEventRepeater() {
+ ForEach(listeners_, Delete<TestEventListener>);
+}
+
+void TestEventRepeater::Append(TestEventListener *listener) {
+ listeners_.push_back(listener);
+}
+
+// TODO(vladl@google.com): Factor the search functionality into Vector::Find.
+TestEventListener* TestEventRepeater::Release(TestEventListener *listener) {
+ for (size_t i = 0; i < listeners_.size(); ++i) {
+ if (listeners_[i] == listener) {
+ listeners_.erase(listeners_.begin() + i);
+ return listener;
+ }
+ }
+
+ return NULL;
+}
+
+// Since most methods are very similar, use macros to reduce boilerplate.
+// This defines a member that forwards the call to all listeners.
+#define GTEST_REPEATER_METHOD_(Name, Type) \
+void TestEventRepeater::Name(const Type& parameter) { \
+ if (forwarding_enabled_) { \
+ for (size_t i = 0; i < listeners_.size(); i++) { \
+ listeners_[i]->Name(parameter); \
+ } \
+ } \
+}
+// This defines a member that forwards the call to all listeners in reverse
+// order.
+#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \
+void TestEventRepeater::Name(const Type& parameter) { \
+ if (forwarding_enabled_) { \
+ for (int i = static_cast<int>(listeners_.size()) - 1; i >= 0; i--) { \
+ listeners_[i]->Name(parameter); \
+ } \
+ } \
+}
+
+GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest)
+GTEST_REPEATER_METHOD_(OnEnvironmentsSetUpStart, UnitTest)
+GTEST_REPEATER_METHOD_(OnTestCaseStart, TestCase)
+GTEST_REPEATER_METHOD_(OnTestStart, TestInfo)
+GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult)
+GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest)
+GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest)
+GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsTearDownEnd, UnitTest)
+GTEST_REVERSE_REPEATER_METHOD_(OnTestEnd, TestInfo)
+GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestCase)
+GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest)
+
+#undef GTEST_REPEATER_METHOD_
+#undef GTEST_REVERSE_REPEATER_METHOD_
+
+void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test,
+ int iteration) {
+ if (forwarding_enabled_) {
+ for (size_t i = 0; i < listeners_.size(); i++) {
+ listeners_[i]->OnTestIterationStart(unit_test, iteration);
+ }
+ }
+}
+
+void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test,
+ int iteration) {
+ if (forwarding_enabled_) {
+ for (int i = static_cast<int>(listeners_.size()) - 1; i >= 0; i--) {
+ listeners_[i]->OnTestIterationEnd(unit_test, iteration);
+ }
+ }
+}
+
+// End TestEventRepeater
+
+// This class generates an XML output file.
+class XmlUnitTestResultPrinter : public EmptyTestEventListener {
+ public:
+ explicit XmlUnitTestResultPrinter(const char* output_file);
+
+ virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);
+
+ private:
+ // Is c a whitespace character that is normalized to a space character
+ // when it appears in an XML attribute value?
+ static bool IsNormalizableWhitespace(char c) {
+ return c == 0x9 || c == 0xA || c == 0xD;
+ }
+
+ // May c appear in a well-formed XML document?
+ static bool IsValidXmlCharacter(char c) {
+ return IsNormalizableWhitespace(c) || c >= 0x20;
+ }
+
+ // Returns an XML-escaped copy of the input string str. If
+ // is_attribute is true, the text is meant to appear as an attribute
+ // value, and normalizable whitespace is preserved by replacing it
+ // with character references.
+ static std::string EscapeXml(const std::string& str, bool is_attribute);
+
+ // Returns the given string with all characters invalid in XML removed.
+ static std::string RemoveInvalidXmlCharacters(const std::string& str);
+
+ // Convenience wrapper around EscapeXml when str is an attribute value.
+ static std::string EscapeXmlAttribute(const std::string& str) {
+ return EscapeXml(str, true);
+ }
+
+ // Convenience wrapper around EscapeXml when str is not an attribute value.
+ static std::string EscapeXmlText(const char* str) {
+ return EscapeXml(str, false);
+ }
+
+ // Verifies that the given attribute belongs to the given element and
+ // streams the attribute as XML.
+ static void OutputXmlAttribute(std::ostream* stream,
+ const std::string& element_name,
+ const std::string& name,
+ const std::string& value);
+
+ // Streams an XML CDATA section, escaping invalid CDATA sequences as needed.
+ static void OutputXmlCDataSection(::std::ostream* stream, const char* data);
+
+ // Streams an XML representation of a TestInfo object.
+ static void OutputXmlTestInfo(::std::ostream* stream,
+ const char* test_case_name,
+ const TestInfo& test_info);
+
+ // Prints an XML representation of a TestCase object
+ static void PrintXmlTestCase(::std::ostream* stream,
+ const TestCase& test_case);
+
+ // Prints an XML summary of unit_test to output stream out.
+ static void PrintXmlUnitTest(::std::ostream* stream,
+ const UnitTest& unit_test);
+
+ // Produces a string representing the test properties in a result as space
+ // delimited XML attributes based on the property key="value" pairs.
+ // When the std::string is not empty, it includes a space at the beginning,
+ // to delimit this attribute from prior attributes.
+ static std::string TestPropertiesAsXmlAttributes(const TestResult& result);
+
+ // The output file.
+ const std::string output_file_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter);
+};
+
+// Creates a new XmlUnitTestResultPrinter.
+XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file)
+ : output_file_(output_file) {
+ if (output_file_.c_str() == NULL || output_file_.empty()) {
+ fprintf(stderr, "XML output file may not be null\n");
+ fflush(stderr);
+ exit(EXIT_FAILURE);
+ }
+}
+
+// Called after the unit test ends.
+void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
+ int /*iteration*/) {
+ FILE* xmlout = NULL;
+ FilePath output_file(output_file_);
+ FilePath output_dir(output_file.RemoveFileName());
+
+ if (output_dir.CreateDirectoriesRecursively()) {
+ xmlout = posix::FOpen(output_file_.c_str(), "w");
+ }
+ if (xmlout == NULL) {
+ // TODO(wan): report the reason of the failure.
+ //
+ // We don't do it for now as:
+ //
+ // 1. There is no urgent need for it.
+ // 2. It's a bit involved to make the errno variable thread-safe on
+ // all three operating systems (Linux, Windows, and Mac OS).
+ // 3. To interpret the meaning of errno in a thread-safe way,
+ // we need the strerror_r() function, which is not available on
+ // Windows.
+ fprintf(stderr,
+ "Unable to open file \"%s\"\n",
+ output_file_.c_str());
+ fflush(stderr);
+ exit(EXIT_FAILURE);
+ }
+ std::stringstream stream;
+ PrintXmlUnitTest(&stream, unit_test);
+ fprintf(xmlout, "%s", StringStreamToString(&stream).c_str());
+ fclose(xmlout);
+}
+
+// Returns an XML-escaped copy of the input string str. If is_attribute
+// is true, the text is meant to appear as an attribute value, and
+// normalizable whitespace is preserved by replacing it with character
+// references.
+//
+// Invalid XML characters in str, if any, are stripped from the output.
+// It is expected that most, if not all, of the text processed by this
+// module will consist of ordinary English text.
+// If this module is ever modified to produce version 1.1 XML output,
+// most invalid characters can be retained using character references.
+// TODO(wan): It might be nice to have a minimally invasive, human-readable
+// escaping scheme for invalid characters, rather than dropping them.
+std::string XmlUnitTestResultPrinter::EscapeXml(
+ const std::string& str, bool is_attribute) {
+ Message m;
+
+ for (size_t i = 0; i < str.size(); ++i) {
+ const char ch = str[i];
+ switch (ch) {
+ case '<':
+ m << "&lt;";
+ break;
+ case '>':
+ m << "&gt;";
+ break;
+ case '&':
+ m << "&amp;";
+ break;
+ case '\'':
+ if (is_attribute)
+ m << "&apos;";
+ else
+ m << '\'';
+ break;
+ case '"':
+ if (is_attribute)
+ m << "&quot;";
+ else
+ m << '"';
+ break;
+ default:
+ if (IsValidXmlCharacter(ch)) {
+ if (is_attribute && IsNormalizableWhitespace(ch))
+ m << "&#x" << String::FormatByte(static_cast<unsigned char>(ch))
+ << ";";
+ else
+ m << ch;
+ }
+ break;
+ }
+ }
+
+ return m.GetString();
+}
+
+// Returns the given string with all characters invalid in XML removed.
+// Currently invalid characters are dropped from the string. An
+// alternative is to replace them with certain characters such as . or ?.
+std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(
+ const std::string& str) {
+ std::string output;
+ output.reserve(str.size());
+ for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
+ if (IsValidXmlCharacter(*it))
+ output.push_back(*it);
+
+ return output;
+}
+
+// The following routines generate an XML representation of a UnitTest
+// object.
+//
+// This is how Google Test concepts map to the DTD:
+//
+// <testsuites name="AllTests"> <-- corresponds to a UnitTest object
+// <testsuite name="testcase-name"> <-- corresponds to a TestCase object
+// <testcase name="test-name"> <-- corresponds to a TestInfo object
+// <failure message="...">...</failure>
+// <failure message="...">...</failure>
+// <failure message="...">...</failure>
+// <-- individual assertion failures
+// </testcase>
+// </testsuite>
+// </testsuites>
+
+// Formats the given time in milliseconds as seconds.
+std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) {
+ ::std::stringstream ss;
+ ss << ms/1000.0;
+ return ss.str();
+}
+
+// Converts the given epoch time in milliseconds to a date string in the ISO
+// 8601 format, without the timezone information.
+std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) {
+ // Using non-reentrant version as localtime_r is not portable.
+ time_t seconds = static_cast<time_t>(ms / 1000);
+#ifdef _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4996) // Temporarily disables warning 4996
+ // (function or variable may be unsafe).
+ const struct tm* const time_struct = localtime(&seconds); // NOLINT
+# pragma warning(pop) // Restores the warning state again.
+#else
+ const struct tm* const time_struct = localtime(&seconds); // NOLINT
+#endif
+ if (time_struct == NULL)
+ return ""; // Invalid ms value
+
+ // YYYY-MM-DDThh:mm:ss
+ return StreamableToString(time_struct->tm_year + 1900) + "-" +
+ String::FormatIntWidth2(time_struct->tm_mon + 1) + "-" +
+ String::FormatIntWidth2(time_struct->tm_mday) + "T" +
+ String::FormatIntWidth2(time_struct->tm_hour) + ":" +
+ String::FormatIntWidth2(time_struct->tm_min) + ":" +
+ String::FormatIntWidth2(time_struct->tm_sec);
+}
+
+// Streams an XML CDATA section, escaping invalid CDATA sequences as needed.
+void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream,
+ const char* data) {
+ const char* segment = data;
+ *stream << "<![CDATA[";
+ for (;;) {
+ const char* const next_segment = strstr(segment, "]]>");
+ if (next_segment != NULL) {
+ stream->write(
+ segment, static_cast<std::streamsize>(next_segment - segment));
+ *stream << "]]>]]&gt;<![CDATA[";
+ segment = next_segment + strlen("]]>");
+ } else {
+ *stream << segment;
+ break;
+ }
+ }
+ *stream << "]]>";
+}
+
+void XmlUnitTestResultPrinter::OutputXmlAttribute(
+ std::ostream* stream,
+ const std::string& element_name,
+ const std::string& name,
+ const std::string& value) {
+ const std::vector<std::string>& allowed_names =
+ GetReservedAttributesForElement(element_name);
+
+ GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) !=
+ allowed_names.end())
+ << "Attribute " << name << " is not allowed for element <" << element_name
+ << ">.";
+
+ *stream << " " << name << "=\"" << EscapeXmlAttribute(value) << "\"";
+}
+
+// Prints an XML representation of a TestInfo object.
+// TODO(wan): There is also value in printing properties with the plain printer.
+void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream,
+ const char* test_case_name,
+ const TestInfo& test_info) {
+ const TestResult& result = *test_info.result();
+ const std::string kTestcase = "testcase";
+
+ *stream << " <testcase";
+ OutputXmlAttribute(stream, kTestcase, "name", test_info.name());
+
+ if (test_info.value_param() != NULL) {
+ OutputXmlAttribute(stream, kTestcase, "value_param",
+ test_info.value_param());
+ }
+ if (test_info.type_param() != NULL) {
+ OutputXmlAttribute(stream, kTestcase, "type_param", test_info.type_param());
+ }
+
+ OutputXmlAttribute(stream, kTestcase, "status",
+ test_info.should_run() ? "run" : "notrun");
+ OutputXmlAttribute(stream, kTestcase, "time",
+ FormatTimeInMillisAsSeconds(result.elapsed_time()));
+ OutputXmlAttribute(stream, kTestcase, "classname", test_case_name);
+ *stream << TestPropertiesAsXmlAttributes(result);
+
+ int failures = 0;
+ for (int i = 0; i < result.total_part_count(); ++i) {
+ const TestPartResult& part = result.GetTestPartResult(i);
+ if (part.failed()) {
+ if (++failures == 1) {
+ *stream << ">\n";
+ }
+ const string location = internal::FormatCompilerIndependentFileLocation(
+ part.file_name(), part.line_number());
+ const string summary = location + "\n" + part.summary();
+ *stream << " <failure message=\""
+ << EscapeXmlAttribute(summary.c_str())
+ << "\" type=\"\">";
+ const string detail = location + "\n" + part.message();
+ OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str());
+ *stream << "</failure>\n";
+ }
+ }
+
+ if (failures == 0)
+ *stream << " />\n";
+ else
+ *stream << " </testcase>\n";
+}
+
+// Prints an XML representation of a TestCase object
+void XmlUnitTestResultPrinter::PrintXmlTestCase(std::ostream* stream,
+ const TestCase& test_case) {
+ const std::string kTestsuite = "testsuite";
+ *stream << " <" << kTestsuite;
+ OutputXmlAttribute(stream, kTestsuite, "name", test_case.name());
+ OutputXmlAttribute(stream, kTestsuite, "tests",
+ StreamableToString(test_case.reportable_test_count()));
+ OutputXmlAttribute(stream, kTestsuite, "failures",
+ StreamableToString(test_case.failed_test_count()));
+ OutputXmlAttribute(
+ stream, kTestsuite, "disabled",
+ StreamableToString(test_case.reportable_disabled_test_count()));
+ OutputXmlAttribute(stream, kTestsuite, "errors", "0");
+ OutputXmlAttribute(stream, kTestsuite, "time",
+ FormatTimeInMillisAsSeconds(test_case.elapsed_time()));
+ *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result())
+ << ">\n";
+
+ for (int i = 0; i < test_case.total_test_count(); ++i) {
+ if (test_case.GetTestInfo(i)->is_reportable())
+ OutputXmlTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i));
+ }
+ *stream << " </" << kTestsuite << ">\n";
+}
+
+// Prints an XML summary of unit_test to output stream out.
+void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream,
+ const UnitTest& unit_test) {
+ const std::string kTestsuites = "testsuites";
+
+ *stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ *stream << "<" << kTestsuites;
+
+ OutputXmlAttribute(stream, kTestsuites, "tests",
+ StreamableToString(unit_test.reportable_test_count()));
+ OutputXmlAttribute(stream, kTestsuites, "failures",
+ StreamableToString(unit_test.failed_test_count()));
+ OutputXmlAttribute(
+ stream, kTestsuites, "disabled",
+ StreamableToString(unit_test.reportable_disabled_test_count()));
+ OutputXmlAttribute(stream, kTestsuites, "errors", "0");
+ OutputXmlAttribute(
+ stream, kTestsuites, "timestamp",
+ FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp()));
+ OutputXmlAttribute(stream, kTestsuites, "time",
+ FormatTimeInMillisAsSeconds(unit_test.elapsed_time()));
+
+ if (GTEST_FLAG(shuffle)) {
+ OutputXmlAttribute(stream, kTestsuites, "random_seed",
+ StreamableToString(unit_test.random_seed()));
+ }
+
+ *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result());
+
+ OutputXmlAttribute(stream, kTestsuites, "name", "AllTests");
+ *stream << ">\n";
+
+ for (int i = 0; i < unit_test.total_test_case_count(); ++i) {
+ if (unit_test.GetTestCase(i)->reportable_test_count() > 0)
+ PrintXmlTestCase(stream, *unit_test.GetTestCase(i));
+ }
+ *stream << "</" << kTestsuites << ">\n";
+}
+
+// Produces a string representing the test properties in a result as space
+// delimited XML attributes based on the property key="value" pairs.
+std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes(
+ const TestResult& result) {
+ Message attributes;
+ for (int i = 0; i < result.test_property_count(); ++i) {
+ const TestProperty& property = result.GetTestProperty(i);
+ attributes << " " << property.key() << "="
+ << "\"" << EscapeXmlAttribute(property.value()) << "\"";
+ }
+ return attributes.GetString();
+}
+
+// End XmlUnitTestResultPrinter
+
+#if GTEST_CAN_STREAM_RESULTS_
+
+// Checks if str contains '=', '&', '%' or '\n' characters. If yes,
+// replaces them by "%xx" where xx is their hexadecimal value. For
+// example, replaces "=" with "%3D". This algorithm is O(strlen(str))
+// in both time and space -- important as the input str may contain an
+// arbitrarily long test failure message and stack trace.
+string StreamingListener::UrlEncode(const char* str) {
+ string result;
+ result.reserve(strlen(str) + 1);
+ for (char ch = *str; ch != '\0'; ch = *++str) {
+ switch (ch) {
+ case '%':
+ case '=':
+ case '&':
+ case '\n':
+ result.append("%" + String::FormatByte(static_cast<unsigned char>(ch)));
+ break;
+ default:
+ result.push_back(ch);
+ break;
+ }
+ }
+ return result;
+}
+
+void StreamingListener::SocketWriter::MakeConnection() {
+ GTEST_CHECK_(sockfd_ == -1)
+ << "MakeConnection() can't be called when there is already a connection.";
+
+ addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses.
+ hints.ai_socktype = SOCK_STREAM;
+ addrinfo* servinfo = NULL;
+
+ // Use the getaddrinfo() to get a linked list of IP addresses for
+ // the given host name.
+ const int error_num = getaddrinfo(
+ host_name_.c_str(), port_num_.c_str(), &hints, &servinfo);
+ if (error_num != 0) {
+ GTEST_LOG_(WARNING) << "stream_result_to: getaddrinfo() failed: "
+ << gai_strerror(error_num);
+ }
+
+ // Loop through all the results and connect to the first we can.
+ for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != NULL;
+ cur_addr = cur_addr->ai_next) {
+ sockfd_ = socket(
+ cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol);
+ if (sockfd_ != -1) {
+ // Connect the client socket to the server socket.
+ if (connect(sockfd_, cur_addr->ai_addr, cur_addr->ai_addrlen) == -1) {
+ close(sockfd_);
+ sockfd_ = -1;
+ }
+ }
+ }
+
+ freeaddrinfo(servinfo); // all done with this structure
+
+ if (sockfd_ == -1) {
+ GTEST_LOG_(WARNING) << "stream_result_to: failed to connect to "
+ << host_name_ << ":" << port_num_;
+ }
+}
+
+// End of class Streaming Listener
+#endif // GTEST_CAN_STREAM_RESULTS__
+
+// Class ScopedTrace
+
+// Pushes the given source file location and message onto a per-thread
+// trace stack maintained by Google Test.
+ScopedTrace::ScopedTrace(const char* file, int line, const Message& message)
+ GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) {
+ TraceInfo trace;
+ trace.file = file;
+ trace.line = line;
+ trace.message = message.GetString();
+
+ UnitTest::GetInstance()->PushGTestTrace(trace);
+}
+
+// Pops the info pushed by the c'tor.
+ScopedTrace::~ScopedTrace()
+ GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) {
+ UnitTest::GetInstance()->PopGTestTrace();
+}
+
+
+// class OsStackTraceGetter
+
+// Returns the current OS stack trace as an std::string. Parameters:
+//
+// max_depth - the maximum number of stack frames to be included
+// in the trace.
+// skip_count - the number of top frames to be skipped; doesn't count
+// against max_depth.
+//
+string OsStackTraceGetter::CurrentStackTrace(int /* max_depth */,
+ int /* skip_count */)
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+ return "";
+}
+
+void OsStackTraceGetter::UponLeavingGTest()
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+}
+
+const char* const
+OsStackTraceGetter::kElidedFramesMarker =
+ "... " GTEST_NAME_ " internal frames ...";
+
+// A helper class that creates the premature-exit file in its
+// constructor and deletes the file in its destructor.
+class ScopedPrematureExitFile {
+ public:
+ explicit ScopedPrematureExitFile(const char* premature_exit_filepath)
+ : premature_exit_filepath_(premature_exit_filepath) {
+ // If a path to the premature-exit file is specified...
+ if (premature_exit_filepath != NULL && *premature_exit_filepath != '\0') {
+ // create the file with a single "0" character in it. I/O
+ // errors are ignored as there's nothing better we can do and we
+ // don't want to fail the test because of this.
+ FILE* pfile = posix::FOpen(premature_exit_filepath, "w");
+ fwrite("0", 1, 1, pfile);
+ fclose(pfile);
+ }
+ }
+
+ ~ScopedPrematureExitFile() {
+ if (premature_exit_filepath_ != NULL && *premature_exit_filepath_ != '\0') {
+ remove(premature_exit_filepath_);
+ }
+ }
+
+ private:
+ const char* const premature_exit_filepath_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile);
+};
+
+} // namespace internal
+
+// class TestEventListeners
+
+TestEventListeners::TestEventListeners()
+ : repeater_(new internal::TestEventRepeater()),
+ default_result_printer_(NULL),
+ default_xml_generator_(NULL) {
+}
+
+TestEventListeners::~TestEventListeners() { delete repeater_; }
+
+// Returns the standard listener responsible for the default console
+// output. Can be removed from the listeners list to shut down default
+// console output. Note that removing this object from the listener list
+// with Release transfers its ownership to the user.
+void TestEventListeners::Append(TestEventListener* listener) {
+ repeater_->Append(listener);
+}
+
+// Removes the given event listener from the list and returns it. It then
+// becomes the caller's responsibility to delete the listener. Returns
+// NULL if the listener is not found in the list.
+TestEventListener* TestEventListeners::Release(TestEventListener* listener) {
+ if (listener == default_result_printer_)
+ default_result_printer_ = NULL;
+ else if (listener == default_xml_generator_)
+ default_xml_generator_ = NULL;
+ return repeater_->Release(listener);
+}
+
+// Returns repeater that broadcasts the TestEventListener events to all
+// subscribers.
+TestEventListener* TestEventListeners::repeater() { return repeater_; }
+
+// Sets the default_result_printer attribute to the provided listener.
+// The listener is also added to the listener list and previous
+// default_result_printer is removed from it and deleted. The listener can
+// also be NULL in which case it will not be added to the list. Does
+// nothing if the previous and the current listener objects are the same.
+void TestEventListeners::SetDefaultResultPrinter(TestEventListener* listener) {
+ if (default_result_printer_ != listener) {
+ // It is an error to pass this method a listener that is already in the
+ // list.
+ delete Release(default_result_printer_);
+ default_result_printer_ = listener;
+ if (listener != NULL)
+ Append(listener);
+ }
+}
+
+// Sets the default_xml_generator attribute to the provided listener. The
+// listener is also added to the listener list and previous
+// default_xml_generator is removed from it and deleted. The listener can
+// also be NULL in which case it will not be added to the list. Does
+// nothing if the previous and the current listener objects are the same.
+void TestEventListeners::SetDefaultXmlGenerator(TestEventListener* listener) {
+ if (default_xml_generator_ != listener) {
+ // It is an error to pass this method a listener that is already in the
+ // list.
+ delete Release(default_xml_generator_);
+ default_xml_generator_ = listener;
+ if (listener != NULL)
+ Append(listener);
+ }
+}
+
+// Controls whether events will be forwarded by the repeater to the
+// listeners in the list.
+bool TestEventListeners::EventForwardingEnabled() const {
+ return repeater_->forwarding_enabled();
+}
+
+void TestEventListeners::SuppressEventForwarding() {
+ repeater_->set_forwarding_enabled(false);
+}
+
+// class UnitTest
+
+// Gets the singleton UnitTest object. The first time this method is
+// called, a UnitTest object is constructed and returned. Consecutive
+// calls will return the same object.
+//
+// We don't protect this under mutex_ as a user is not supposed to
+// call this before main() starts, from which point on the return
+// value will never change.
+UnitTest* UnitTest::GetInstance() {
+ // When compiled with MSVC 7.1 in optimized mode, destroying the
+ // UnitTest object upon exiting the program messes up the exit code,
+ // causing successful tests to appear failed. We have to use a
+ // different implementation in this case to bypass the compiler bug.
+ // This implementation makes the compiler happy, at the cost of
+ // leaking the UnitTest object.
+
+ // CodeGear C++Builder insists on a public destructor for the
+ // default implementation. Use this implementation to keep good OO
+ // design with private destructor.
+
+#if (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__)
+ static UnitTest* const instance = new UnitTest;
+ return instance;
+#else
+ static UnitTest instance;
+ return &instance;
+#endif // (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__)
+}
+
+// Gets the number of successful test cases.
+int UnitTest::successful_test_case_count() const {
+ return impl()->successful_test_case_count();
+}
+
+// Gets the number of failed test cases.
+int UnitTest::failed_test_case_count() const {
+ return impl()->failed_test_case_count();
+}
+
+// Gets the number of all test cases.
+int UnitTest::total_test_case_count() const {
+ return impl()->total_test_case_count();
+}
+
+// Gets the number of all test cases that contain at least one test
+// that should run.
+int UnitTest::test_case_to_run_count() const {
+ return impl()->test_case_to_run_count();
+}
+
+// Gets the number of successful tests.
+int UnitTest::successful_test_count() const {
+ return impl()->successful_test_count();
+}
+
+// Gets the number of failed tests.
+int UnitTest::failed_test_count() const { return impl()->failed_test_count(); }
+
+// Gets the number of disabled tests that will be reported in the XML report.
+int UnitTest::reportable_disabled_test_count() const {
+ return impl()->reportable_disabled_test_count();
+}
+
+// Gets the number of disabled tests.
+int UnitTest::disabled_test_count() const {
+ return impl()->disabled_test_count();
+}
+
+// Gets the number of tests to be printed in the XML report.
+int UnitTest::reportable_test_count() const {
+ return impl()->reportable_test_count();
+}
+
+// Gets the number of all tests.
+int UnitTest::total_test_count() const { return impl()->total_test_count(); }
+
+// Gets the number of tests that should run.
+int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); }
+
+// Gets the time of the test program start, in ms from the start of the
+// UNIX epoch.
+internal::TimeInMillis UnitTest::start_timestamp() const {
+ return impl()->start_timestamp();
+}
+
+// Gets the elapsed time, in milliseconds.
+internal::TimeInMillis UnitTest::elapsed_time() const {
+ return impl()->elapsed_time();
+}
+
+// Returns true iff the unit test passed (i.e. all test cases passed).
+bool UnitTest::Passed() const { return impl()->Passed(); }
+
+// Returns true iff the unit test failed (i.e. some test case failed
+// or something outside of all tests failed).
+bool UnitTest::Failed() const { return impl()->Failed(); }
+
+// Gets the i-th test case among all the test cases. i can range from 0 to
+// total_test_case_count() - 1. If i is not in that range, returns NULL.
+const TestCase* UnitTest::GetTestCase(int i) const {
+ return impl()->GetTestCase(i);
+}
+
+// Returns the TestResult containing information on test failures and
+// properties logged outside of individual test cases.
+const TestResult& UnitTest::ad_hoc_test_result() const {
+ return *impl()->ad_hoc_test_result();
+}
+
+// Gets the i-th test case among all the test cases. i can range from 0 to
+// total_test_case_count() - 1. If i is not in that range, returns NULL.
+TestCase* UnitTest::GetMutableTestCase(int i) {
+ return impl()->GetMutableTestCase(i);
+}
+
+// Returns the list of event listeners that can be used to track events
+// inside Google Test.
+TestEventListeners& UnitTest::listeners() {
+ return *impl()->listeners();
+}
+
+// Registers and returns a global test environment. When a test
+// program is run, all global test environments will be set-up in the
+// order they were registered. After all tests in the program have
+// finished, all global test environments will be torn-down in the
+// *reverse* order they were registered.
+//
+// The UnitTest object takes ownership of the given environment.
+//
+// We don't protect this under mutex_, as we only support calling it
+// from the main thread.
+Environment* UnitTest::AddEnvironment(Environment* env) {
+ if (env == NULL) {
+ return NULL;
+ }
+
+ impl_->environments().push_back(env);
+ return env;
+}
+
+// Adds a TestPartResult to the current TestResult object. All Google Test
+// assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call
+// this to report their results. The user code should use the
+// assertion macros instead of calling this directly.
+void UnitTest::AddTestPartResult(
+ TestPartResult::Type result_type,
+ const char* file_name,
+ int line_number,
+ const std::string& message,
+ const std::string& os_stack_trace) GTEST_LOCK_EXCLUDED_(mutex_) {
+ Message msg;
+ msg << message;
+
+ internal::MutexLock lock(&mutex_);
+ if (impl_->gtest_trace_stack().size() > 0) {
+ msg << "\n" << GTEST_NAME_ << " trace:";
+
+ for (int i = static_cast<int>(impl_->gtest_trace_stack().size());
+ i > 0; --i) {
+ const internal::TraceInfo& trace = impl_->gtest_trace_stack()[i - 1];
+ msg << "\n" << internal::FormatFileLocation(trace.file, trace.line)
+ << " " << trace.message;
+ }
+ }
+
+ if (os_stack_trace.c_str() != NULL && !os_stack_trace.empty()) {
+ msg << internal::kStackTraceMarker << os_stack_trace;
+ }
+
+ const TestPartResult result =
+ TestPartResult(result_type, file_name, line_number,
+ msg.GetString().c_str());
+ impl_->GetTestPartResultReporterForCurrentThread()->
+ ReportTestPartResult(result);
+
+ if (result_type != TestPartResult::kSuccess) {
+ // gtest_break_on_failure takes precedence over
+ // gtest_throw_on_failure. This allows a user to set the latter
+ // in the code (perhaps in order to use Google Test assertions
+ // with another testing framework) and specify the former on the
+ // command line for debugging.
+ if (GTEST_FLAG(break_on_failure)) {
+#if GTEST_OS_WINDOWS
+ // Using DebugBreak on Windows allows gtest to still break into a debugger
+ // when a failure happens and both the --gtest_break_on_failure and
+ // the --gtest_catch_exceptions flags are specified.
+ DebugBreak();
+#else
+ // Dereference NULL through a volatile pointer to prevent the compiler
+ // from removing. We use this rather than abort() or __builtin_trap() for
+ // portability: Symbian doesn't implement abort() well, and some debuggers
+ // don't correctly trap abort().
+ *static_cast<volatile int*>(NULL) = 1;
+#endif // GTEST_OS_WINDOWS
+ } else if (GTEST_FLAG(throw_on_failure)) {
+#if GTEST_HAS_EXCEPTIONS
+ throw internal::GoogleTestFailureException(result);
+#else
+ // We cannot call abort() as it generates a pop-up in debug mode
+ // that cannot be suppressed in VC 7.1 or below.
+ exit(1);
+#endif
+ }
+ }
+}
+
+// Adds a TestProperty to the current TestResult object when invoked from
+// inside a test, to current TestCase's ad_hoc_test_result_ when invoked
+// from SetUpTestCase or TearDownTestCase, or to the global property set
+// when invoked elsewhere. If the result already contains a property with
+// the same key, the value will be updated.
+void UnitTest::RecordProperty(const std::string& key,
+ const std::string& value) {
+ impl_->RecordProperty(TestProperty(key, value));
+}
+
+// Runs all tests in this UnitTest object and prints the result.
+// Returns 0 if successful, or 1 otherwise.
+//
+// We don't protect this under mutex_, as we only support calling it
+// from the main thread.
+int UnitTest::Run() {
+ const bool in_death_test_child_process =
+ internal::GTEST_FLAG(internal_run_death_test).length() > 0;
+
+ // Google Test implements this protocol for catching that a test
+ // program exits before returning control to Google Test:
+ //
+ // 1. Upon start, Google Test creates a file whose absolute path
+ // is specified by the environment variable
+ // TEST_PREMATURE_EXIT_FILE.
+ // 2. When Google Test has finished its work, it deletes the file.
+ //
+ // This allows a test runner to set TEST_PREMATURE_EXIT_FILE before
+ // running a Google-Test-based test program and check the existence
+ // of the file at the end of the test execution to see if it has
+ // exited prematurely.
+
+ // If we are in the child process of a death test, don't
+ // create/delete the premature exit file, as doing so is unnecessary
+ // and will confuse the parent process. Otherwise, create/delete
+ // the file upon entering/leaving this function. If the program
+ // somehow exits before this function has a chance to return, the
+ // premature-exit file will be left undeleted, causing a test runner
+ // that understands the premature-exit-file protocol to report the
+ // test as having failed.
+ const internal::ScopedPrematureExitFile premature_exit_file(
+ in_death_test_child_process ?
+ NULL : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE"));
+
+ // Captures the value of GTEST_FLAG(catch_exceptions). This value will be
+ // used for the duration of the program.
+ impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions));
+
+#if GTEST_HAS_SEH
+ // Either the user wants Google Test to catch exceptions thrown by the
+ // tests or this is executing in the context of death test child
+ // process. In either case the user does not want to see pop-up dialogs
+ // about crashes - they are expected.
+ if (impl()->catch_exceptions() || in_death_test_child_process) {
+# if !GTEST_OS_WINDOWS_MOBILE
+ // SetErrorMode doesn't exist on CE.
+ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT |
+ SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
+# endif // !GTEST_OS_WINDOWS_MOBILE
+
+# if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE
+ // Death test children can be terminated with _abort(). On Windows,
+ // _abort() can show a dialog with a warning message. This forces the
+ // abort message to go to stderr instead.
+ _set_error_mode(_OUT_TO_STDERR);
+# endif
+
+# if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE
+ // In the debug version, Visual Studio pops up a separate dialog
+ // offering a choice to debug the aborted program. We need to suppress
+ // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement
+ // executed. Google Test will notify the user of any unexpected
+ // failure via stderr.
+ //
+ // VC++ doesn't define _set_abort_behavior() prior to the version 8.0.
+ // Users of prior VC versions shall suffer the agony and pain of
+ // clicking through the countless debug dialogs.
+ // TODO(vladl@google.com): find a way to suppress the abort dialog() in the
+ // debug mode when compiled with VC 7.1 or lower.
+ if (!GTEST_FLAG(break_on_failure))
+ _set_abort_behavior(
+ 0x0, // Clear the following flags:
+ _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump.
+# endif
+ }
+#endif // GTEST_HAS_SEH
+
+ return internal::HandleExceptionsInMethodIfSupported(
+ impl(),
+ &internal::UnitTestImpl::RunAllTests,
+ "auxiliary test code (environments or event listeners)") ? 0 : 1;
+}
+
+// Returns the working directory when the first TEST() or TEST_F() was
+// executed.
+const char* UnitTest::original_working_dir() const {
+ return impl_->original_working_dir_.c_str();
+}
+
+// Returns the TestCase object for the test that's currently running,
+// or NULL if no test is running.
+const TestCase* UnitTest::current_test_case() const
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+ internal::MutexLock lock(&mutex_);
+ return impl_->current_test_case();
+}
+
+// Returns the TestInfo object for the test that's currently running,
+// or NULL if no test is running.
+const TestInfo* UnitTest::current_test_info() const
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+ internal::MutexLock lock(&mutex_);
+ return impl_->current_test_info();
+}
+
+// Returns the random seed used at the start of the current test run.
+int UnitTest::random_seed() const { return impl_->random_seed(); }
+
+#if GTEST_HAS_PARAM_TEST
+// Returns ParameterizedTestCaseRegistry object used to keep track of
+// value-parameterized tests and instantiate and register them.
+internal::ParameterizedTestCaseRegistry&
+ UnitTest::parameterized_test_registry()
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+ return impl_->parameterized_test_registry();
+}
+#endif // GTEST_HAS_PARAM_TEST
+
+// Creates an empty UnitTest.
+UnitTest::UnitTest() {
+ impl_ = new internal::UnitTestImpl(this);
+}
+
+// Destructor of UnitTest.
+UnitTest::~UnitTest() {
+ delete impl_;
+}
+
+// Pushes a trace defined by SCOPED_TRACE() on to the per-thread
+// Google Test trace stack.
+void UnitTest::PushGTestTrace(const internal::TraceInfo& trace)
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+ internal::MutexLock lock(&mutex_);
+ impl_->gtest_trace_stack().push_back(trace);
+}
+
+// Pops a trace from the per-thread Google Test trace stack.
+void UnitTest::PopGTestTrace()
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+ internal::MutexLock lock(&mutex_);
+ impl_->gtest_trace_stack().pop_back();
+}
+
+namespace internal {
+
+UnitTestImpl::UnitTestImpl(UnitTest* parent)
+ : parent_(parent),
+#ifdef _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4355) // Temporarily disables warning 4355
+ // (using this in initializer).
+ default_global_test_part_result_reporter_(this),
+ default_per_thread_test_part_result_reporter_(this),
+# pragma warning(pop) // Restores the warning state again.
+#else
+ default_global_test_part_result_reporter_(this),
+ default_per_thread_test_part_result_reporter_(this),
+#endif // _MSC_VER
+ global_test_part_result_repoter_(
+ &default_global_test_part_result_reporter_),
+ per_thread_test_part_result_reporter_(
+ &default_per_thread_test_part_result_reporter_),
+#if GTEST_HAS_PARAM_TEST
+ parameterized_test_registry_(),
+ parameterized_tests_registered_(false),
+#endif // GTEST_HAS_PARAM_TEST
+ last_death_test_case_(-1),
+ current_test_case_(NULL),
+ current_test_info_(NULL),
+ ad_hoc_test_result_(),
+ os_stack_trace_getter_(NULL),
+ post_flag_parse_init_performed_(false),
+ random_seed_(0), // Will be overridden by the flag before first use.
+ random_(0), // Will be reseeded before first use.
+ start_timestamp_(0),
+ elapsed_time_(0),
+#if GTEST_HAS_DEATH_TEST
+ death_test_factory_(new DefaultDeathTestFactory),
+#endif
+ // Will be overridden by the flag before first use.
+ catch_exceptions_(false) {
+ listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter);
+}
+
+UnitTestImpl::~UnitTestImpl() {
+ // Deletes every TestCase.
+ ForEach(test_cases_, internal::Delete<TestCase>);
+
+ // Deletes every Environment.
+ ForEach(environments_, internal::Delete<Environment>);
+
+ delete os_stack_trace_getter_;
+}
+
+// Adds a TestProperty to the current TestResult object when invoked in a
+// context of a test, to current test case's ad_hoc_test_result when invoke
+// from SetUpTestCase/TearDownTestCase, or to the global property set
+// otherwise. If the result already contains a property with the same key,
+// the value will be updated.
+void UnitTestImpl::RecordProperty(const TestProperty& test_property) {
+ std::string xml_element;
+ TestResult* test_result; // TestResult appropriate for property recording.
+
+ if (current_test_info_ != NULL) {
+ xml_element = "testcase";
+ test_result = &(current_test_info_->result_);
+ } else if (current_test_case_ != NULL) {
+ xml_element = "testsuite";
+ test_result = &(current_test_case_->ad_hoc_test_result_);
+ } else {
+ xml_element = "testsuites";
+ test_result = &ad_hoc_test_result_;
+ }
+ test_result->RecordProperty(xml_element, test_property);
+}
+
+#if GTEST_HAS_DEATH_TEST
+// Disables event forwarding if the control is currently in a death test
+// subprocess. Must not be called before InitGoogleTest.
+void UnitTestImpl::SuppressTestEventsIfInSubprocess() {
+ if (internal_run_death_test_flag_.get() != NULL)
+ listeners()->SuppressEventForwarding();
+}
+#endif // GTEST_HAS_DEATH_TEST
+
+// Initializes event listeners performing XML output as specified by
+// UnitTestOptions. Must not be called before InitGoogleTest.
+void UnitTestImpl::ConfigureXmlOutput() {
+ const std::string& output_format = UnitTestOptions::GetOutputFormat();
+ if (output_format == "xml") {
+ listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter(
+ UnitTestOptions::GetAbsolutePathToOutputFile().c_str()));
+ } else if (output_format != "") {
+ printf("WARNING: unrecognized output format \"%s\" ignored.\n",
+ output_format.c_str());
+ fflush(stdout);
+ }
+}
+
+#if GTEST_CAN_STREAM_RESULTS_
+// Initializes event listeners for streaming test results in string form.
+// Must not be called before InitGoogleTest.
+void UnitTestImpl::ConfigureStreamingOutput() {
+ const std::string& target = GTEST_FLAG(stream_result_to);
+ if (!target.empty()) {
+ const size_t pos = target.find(':');
+ if (pos != std::string::npos) {
+ listeners()->Append(new StreamingListener(target.substr(0, pos),
+ target.substr(pos+1)));
+ } else {
+ printf("WARNING: unrecognized streaming target \"%s\" ignored.\n",
+ target.c_str());
+ fflush(stdout);
+ }
+ }
+}
+#endif // GTEST_CAN_STREAM_RESULTS_
+
+// Performs initialization dependent upon flag values obtained in
+// ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to
+// ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest
+// this function is also called from RunAllTests. Since this function can be
+// called more than once, it has to be idempotent.
+void UnitTestImpl::PostFlagParsingInit() {
+ // Ensures that this function does not execute more than once.
+ if (!post_flag_parse_init_performed_) {
+ post_flag_parse_init_performed_ = true;
+
+#if GTEST_HAS_DEATH_TEST
+ InitDeathTestSubprocessControlInfo();
+ SuppressTestEventsIfInSubprocess();
+#endif // GTEST_HAS_DEATH_TEST
+
+ // Registers parameterized tests. This makes parameterized tests
+ // available to the UnitTest reflection API without running
+ // RUN_ALL_TESTS.
+ RegisterParameterizedTests();
+
+ // Configures listeners for XML output. This makes it possible for users
+ // to shut down the default XML output before invoking RUN_ALL_TESTS.
+ ConfigureXmlOutput();
+
+#if GTEST_CAN_STREAM_RESULTS_
+ // Configures listeners for streaming test results to the specified server.
+ ConfigureStreamingOutput();
+#endif // GTEST_CAN_STREAM_RESULTS_
+ }
+}
+
+// A predicate that checks the name of a TestCase against a known
+// value.
+//
+// This is used for implementation of the UnitTest class only. We put
+// it in the anonymous namespace to prevent polluting the outer
+// namespace.
+//
+// TestCaseNameIs is copyable.
+class TestCaseNameIs {
+ public:
+ // Constructor.
+ explicit TestCaseNameIs(const std::string& name)
+ : name_(name) {}
+
+ // Returns true iff the name of test_case matches name_.
+ bool operator()(const TestCase* test_case) const {
+ return test_case != NULL && strcmp(test_case->name(), name_.c_str()) == 0;
+ }
+
+ private:
+ std::string name_;
+};
+
+// Finds and returns a TestCase with the given name. If one doesn't
+// exist, creates one and returns it. It's the CALLER'S
+// RESPONSIBILITY to ensure that this function is only called WHEN THE
+// TESTS ARE NOT SHUFFLED.
+//
+// Arguments:
+//
+// test_case_name: name of the test case
+// type_param: the name of the test case's type parameter, or NULL if
+// this is not a typed or a type-parameterized test case.
+// set_up_tc: pointer to the function that sets up the test case
+// tear_down_tc: pointer to the function that tears down the test case
+TestCase* UnitTestImpl::GetTestCase(const char* test_case_name,
+ const char* type_param,
+ Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc) {
+ // Can we find a TestCase with the given name?
+ const std::vector<TestCase*>::const_iterator test_case =
+ std::find_if(test_cases_.begin(), test_cases_.end(),
+ TestCaseNameIs(test_case_name));
+
+ if (test_case != test_cases_.end())
+ return *test_case;
+
+ // No. Let's create one.
+ TestCase* const new_test_case =
+ new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc);
+
+ // Is this a death test case?
+ if (internal::UnitTestOptions::MatchesFilter(test_case_name,
+ kDeathTestCaseFilter)) {
+ // Yes. Inserts the test case after the last death test case
+ // defined so far. This only works when the test cases haven't
+ // been shuffled. Otherwise we may end up running a death test
+ // after a non-death test.
+ ++last_death_test_case_;
+ test_cases_.insert(test_cases_.begin() + last_death_test_case_,
+ new_test_case);
+ } else {
+ // No. Appends to the end of the list.
+ test_cases_.push_back(new_test_case);
+ }
+
+ test_case_indices_.push_back(static_cast<int>(test_case_indices_.size()));
+ return new_test_case;
+}
+
+// Helpers for setting up / tearing down the given environment. They
+// are for use in the ForEach() function.
+static void SetUpEnvironment(Environment* env) { env->SetUp(); }
+static void TearDownEnvironment(Environment* env) { env->TearDown(); }
+
+// Runs all tests in this UnitTest object, prints the result, and
+// returns true if all tests are successful. If any exception is
+// thrown during a test, the test is considered to be failed, but the
+// rest of the tests will still be run.
+//
+// When parameterized tests are enabled, it expands and registers
+// parameterized tests first in RegisterParameterizedTests().
+// All other functions called from RunAllTests() may safely assume that
+// parameterized tests are ready to be counted and run.
+bool UnitTestImpl::RunAllTests() {
+ // Makes sure InitGoogleTest() was called.
+ if (!GTestIsInitialized()) {
+ printf("%s",
+ "\nThis test program did NOT call ::testing::InitGoogleTest "
+ "before calling RUN_ALL_TESTS(). Please fix it.\n");
+ return false;
+ }
+
+ // Do not run any test if the --help flag was specified.
+ if (g_help_flag)
+ return true;
+
+ // Repeats the call to the post-flag parsing initialization in case the
+ // user didn't call InitGoogleTest.
+ PostFlagParsingInit();
+
+ // Even if sharding is not on, test runners may want to use the
+ // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding
+ // protocol.
+ internal::WriteToShardStatusFileIfNeeded();
+
+ // True iff we are in a subprocess for running a thread-safe-style
+ // death test.
+ bool in_subprocess_for_death_test = false;
+
+#if GTEST_HAS_DEATH_TEST
+ in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL);
+#endif // GTEST_HAS_DEATH_TEST
+
+ const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex,
+ in_subprocess_for_death_test);
+
+ // Compares the full test names with the filter to decide which
+ // tests to run.
+ const bool has_tests_to_run = FilterTests(should_shard
+ ? HONOR_SHARDING_PROTOCOL
+ : IGNORE_SHARDING_PROTOCOL) > 0;
+
+ // Lists the tests and exits if the --gtest_list_tests flag was specified.
+ if (GTEST_FLAG(list_tests)) {
+ // This must be called *after* FilterTests() has been called.
+ ListTestsMatchingFilter();
+ return true;
+ }
+
+ random_seed_ = GTEST_FLAG(shuffle) ?
+ GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0;
+
+ // True iff at least one test has failed.
+ bool failed = false;
+
+ TestEventListener* repeater = listeners()->repeater();
+
+ start_timestamp_ = GetTimeInMillis();
+ repeater->OnTestProgramStart(*parent_);
+
+ // How many times to repeat the tests? We don't want to repeat them
+ // when we are inside the subprocess of a death test.
+ const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat);
+ // Repeats forever if the repeat count is negative.
+ const bool forever = repeat < 0;
+ for (int i = 0; forever || i != repeat; i++) {
+ // We want to preserve failures generated by ad-hoc test
+ // assertions executed before RUN_ALL_TESTS().
+ ClearNonAdHocTestResult();
+
+ const TimeInMillis start = GetTimeInMillis();
+
+ // Shuffles test cases and tests if requested.
+ if (has_tests_to_run && GTEST_FLAG(shuffle)) {
+ random()->Reseed(random_seed_);
+ // This should be done before calling OnTestIterationStart(),
+ // such that a test event listener can see the actual test order
+ // in the event.
+ ShuffleTests();
+ }
+
+ // Tells the unit test event listeners that the tests are about to start.
+ repeater->OnTestIterationStart(*parent_, i);
+
+ // Runs each test case if there is at least one test to run.
+ if (has_tests_to_run) {
+ // Sets up all environments beforehand.
+ repeater->OnEnvironmentsSetUpStart(*parent_);
+ ForEach(environments_, SetUpEnvironment);
+ repeater->OnEnvironmentsSetUpEnd(*parent_);
+
+ // Runs the tests only if there was no fatal failure during global
+ // set-up.
+ if (!Test::HasFatalFailure()) {
+ for (int test_index = 0; test_index < total_test_case_count();
+ test_index++) {
+ GetMutableTestCase(test_index)->Run();
+ }
+ }
+
+ // Tears down all environments in reverse order afterwards.
+ repeater->OnEnvironmentsTearDownStart(*parent_);
+ std::for_each(environments_.rbegin(), environments_.rend(),
+ TearDownEnvironment);
+ repeater->OnEnvironmentsTearDownEnd(*parent_);
+ }
+
+ elapsed_time_ = GetTimeInMillis() - start;
+
+ // Tells the unit test event listener that the tests have just finished.
+ repeater->OnTestIterationEnd(*parent_, i);
+
+ // Gets the result and clears it.
+ if (!Passed()) {
+ failed = true;
+ }
+
+ // Restores the original test order after the iteration. This
+ // allows the user to quickly repro a failure that happens in the
+ // N-th iteration without repeating the first (N - 1) iterations.
+ // This is not enclosed in "if (GTEST_FLAG(shuffle)) { ... }", in
+ // case the user somehow changes the value of the flag somewhere
+ // (it's always safe to unshuffle the tests).
+ UnshuffleTests();
+
+ if (GTEST_FLAG(shuffle)) {
+ // Picks a new random seed for each iteration.
+ random_seed_ = GetNextRandomSeed(random_seed_);
+ }
+ }
+
+ repeater->OnTestProgramEnd(*parent_);
+
+ return !failed;
+}
+
+// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file
+// if the variable is present. If a file already exists at this location, this
+// function will write over it. If the variable is present, but the file cannot
+// be created, prints an error and exits.
+void WriteToShardStatusFileIfNeeded() {
+ const char* const test_shard_file = posix::GetEnv(kTestShardStatusFile);
+ if (test_shard_file != NULL) {
+ FILE* const file = posix::FOpen(test_shard_file, "w");
+ if (file == NULL) {
+ ColoredPrintf(COLOR_RED,
+ "Could not write to the test shard status file \"%s\" "
+ "specified by the %s environment variable.\n",
+ test_shard_file, kTestShardStatusFile);
+ fflush(stdout);
+ exit(EXIT_FAILURE);
+ }
+ fclose(file);
+ }
+}
+
+// Checks whether sharding is enabled by examining the relevant
+// environment variable values. If the variables are present,
+// but inconsistent (i.e., shard_index >= total_shards), prints
+// an error and exits. If in_subprocess_for_death_test, sharding is
+// disabled because it must only be applied to the original test
+// process. Otherwise, we could filter out death tests we intended to execute.
+bool ShouldShard(const char* total_shards_env,
+ const char* shard_index_env,
+ bool in_subprocess_for_death_test) {
+ if (in_subprocess_for_death_test) {
+ return false;
+ }
+
+ const Int32 total_shards = Int32FromEnvOrDie(total_shards_env, -1);
+ const Int32 shard_index = Int32FromEnvOrDie(shard_index_env, -1);
+
+ if (total_shards == -1 && shard_index == -1) {
+ return false;
+ } else if (total_shards == -1 && shard_index != -1) {
+ const Message msg = Message()
+ << "Invalid environment variables: you have "
+ << kTestShardIndex << " = " << shard_index
+ << ", but have left " << kTestTotalShards << " unset.\n";
+ ColoredPrintf(COLOR_RED, msg.GetString().c_str());
+ fflush(stdout);
+ exit(EXIT_FAILURE);
+ } else if (total_shards != -1 && shard_index == -1) {
+ const Message msg = Message()
+ << "Invalid environment variables: you have "
+ << kTestTotalShards << " = " << total_shards
+ << ", but have left " << kTestShardIndex << " unset.\n";
+ ColoredPrintf(COLOR_RED, msg.GetString().c_str());
+ fflush(stdout);
+ exit(EXIT_FAILURE);
+ } else if (shard_index < 0 || shard_index >= total_shards) {
+ const Message msg = Message()
+ << "Invalid environment variables: we require 0 <= "
+ << kTestShardIndex << " < " << kTestTotalShards
+ << ", but you have " << kTestShardIndex << "=" << shard_index
+ << ", " << kTestTotalShards << "=" << total_shards << ".\n";
+ ColoredPrintf(COLOR_RED, msg.GetString().c_str());
+ fflush(stdout);
+ exit(EXIT_FAILURE);
+ }
+
+ return total_shards > 1;
+}
+
+// Parses the environment variable var as an Int32. If it is unset,
+// returns default_val. If it is not an Int32, prints an error
+// and aborts.
+Int32 Int32FromEnvOrDie(const char* var, Int32 default_val) {
+ const char* str_val = posix::GetEnv(var);
+ if (str_val == NULL) {
+ return default_val;
+ }
+
+ Int32 result;
+ if (!ParseInt32(Message() << "The value of environment variable " << var,
+ str_val, &result)) {
+ exit(EXIT_FAILURE);
+ }
+ return result;
+}
+
+// Given the total number of shards, the shard index, and the test id,
+// returns true iff the test should be run on this shard. The test id is
+// some arbitrary but unique non-negative integer assigned to each test
+// method. Assumes that 0 <= shard_index < total_shards.
+bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) {
+ return (test_id % total_shards) == shard_index;
+}
+
+// Compares the name of each test with the user-specified filter to
+// decide whether the test should be run, then records the result in
+// each TestCase and TestInfo object.
+// If shard_tests == true, further filters tests based on sharding
+// variables in the environment - see
+// http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide.
+// Returns the number of tests that should run.
+int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) {
+ const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ?
+ Int32FromEnvOrDie(kTestTotalShards, -1) : -1;
+ const Int32 shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ?
+ Int32FromEnvOrDie(kTestShardIndex, -1) : -1;
+
+ // num_runnable_tests are the number of tests that will
+ // run across all shards (i.e., match filter and are not disabled).
+ // num_selected_tests are the number of tests to be run on
+ // this shard.
+ int num_runnable_tests = 0;
+ int num_selected_tests = 0;
+ for (size_t i = 0; i < test_cases_.size(); i++) {
+ TestCase* const test_case = test_cases_[i];
+ const std::string &test_case_name = test_case->name();
+ test_case->set_should_run(false);
+
+ for (size_t j = 0; j < test_case->test_info_list().size(); j++) {
+ TestInfo* const test_info = test_case->test_info_list()[j];
+ const std::string test_name(test_info->name());
+ // A test is disabled if test case name or test name matches
+ // kDisableTestFilter.
+ const bool is_disabled =
+ internal::UnitTestOptions::MatchesFilter(test_case_name,
+ kDisableTestFilter) ||
+ internal::UnitTestOptions::MatchesFilter(test_name,
+ kDisableTestFilter);
+ test_info->is_disabled_ = is_disabled;
+
+ const bool matches_filter =
+ internal::UnitTestOptions::FilterMatchesTest(test_case_name,
+ test_name);
+ test_info->matches_filter_ = matches_filter;
+
+ const bool is_runnable =
+ (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) &&
+ matches_filter;
+
+ const bool is_selected = is_runnable &&
+ (shard_tests == IGNORE_SHARDING_PROTOCOL ||
+ ShouldRunTestOnShard(total_shards, shard_index,
+ num_runnable_tests));
+
+ num_runnable_tests += is_runnable;
+ num_selected_tests += is_selected;
+
+ test_info->should_run_ = is_selected;
+ test_case->set_should_run(test_case->should_run() || is_selected);
+ }
+ }
+ return num_selected_tests;
+}
+
+// Prints the given C-string on a single line by replacing all '\n'
+// characters with string "\\n". If the output takes more than
+// max_length characters, only prints the first max_length characters
+// and "...".
+static void PrintOnOneLine(const char* str, int max_length) {
+ if (str != NULL) {
+ for (int i = 0; *str != '\0'; ++str) {
+ if (i >= max_length) {
+ printf("...");
+ break;
+ }
+ if (*str == '\n') {
+ printf("\\n");
+ i += 2;
+ } else {
+ printf("%c", *str);
+ ++i;
+ }
+ }
+ }
+}
+
+// Prints the names of the tests matching the user-specified filter flag.
+void UnitTestImpl::ListTestsMatchingFilter() {
+ // Print at most this many characters for each type/value parameter.
+ const int kMaxParamLength = 250;
+
+ for (size_t i = 0; i < test_cases_.size(); i++) {
+ const TestCase* const test_case = test_cases_[i];
+ bool printed_test_case_name = false;
+
+ for (size_t j = 0; j < test_case->test_info_list().size(); j++) {
+ const TestInfo* const test_info =
+ test_case->test_info_list()[j];
+ if (test_info->matches_filter_) {
+ if (!printed_test_case_name) {
+ printed_test_case_name = true;
+ printf("%s.", test_case->name());
+ if (test_case->type_param() != NULL) {
+ printf(" # %s = ", kTypeParamLabel);
+ // We print the type parameter on a single line to make
+ // the output easy to parse by a program.
+ PrintOnOneLine(test_case->type_param(), kMaxParamLength);
+ }
+ printf("\n");
+ }
+ printf(" %s", test_info->name());
+ if (test_info->value_param() != NULL) {
+ printf(" # %s = ", kValueParamLabel);
+ // We print the value parameter on a single line to make the
+ // output easy to parse by a program.
+ PrintOnOneLine(test_info->value_param(), kMaxParamLength);
+ }
+ printf("\n");
+ }
+ }
+ }
+ fflush(stdout);
+}
+
+// Sets the OS stack trace getter.
+//
+// Does nothing if the input and the current OS stack trace getter are
+// the same; otherwise, deletes the old getter and makes the input the
+// current getter.
+void UnitTestImpl::set_os_stack_trace_getter(
+ OsStackTraceGetterInterface* getter) {
+ if (os_stack_trace_getter_ != getter) {
+ delete os_stack_trace_getter_;
+ os_stack_trace_getter_ = getter;
+ }
+}
+
+// Returns the current OS stack trace getter if it is not NULL;
+// otherwise, creates an OsStackTraceGetter, makes it the current
+// getter, and returns it.
+OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() {
+ if (os_stack_trace_getter_ == NULL) {
+ os_stack_trace_getter_ = new OsStackTraceGetter;
+ }
+
+ return os_stack_trace_getter_;
+}
+
+// Returns the TestResult for the test that's currently running, or
+// the TestResult for the ad hoc test if no test is running.
+TestResult* UnitTestImpl::current_test_result() {
+ return current_test_info_ ?
+ &(current_test_info_->result_) : &ad_hoc_test_result_;
+}
+
+// Shuffles all test cases, and the tests within each test case,
+// making sure that death tests are still run first.
+void UnitTestImpl::ShuffleTests() {
+ // Shuffles the death test cases.
+ ShuffleRange(random(), 0, last_death_test_case_ + 1, &test_case_indices_);
+
+ // Shuffles the non-death test cases.
+ ShuffleRange(random(), last_death_test_case_ + 1,
+ static_cast<int>(test_cases_.size()), &test_case_indices_);
+
+ // Shuffles the tests inside each test case.
+ for (size_t i = 0; i < test_cases_.size(); i++) {
+ test_cases_[i]->ShuffleTests(random());
+ }
+}
+
+// Restores the test cases and tests to their order before the first shuffle.
+void UnitTestImpl::UnshuffleTests() {
+ for (size_t i = 0; i < test_cases_.size(); i++) {
+ // Unshuffles the tests in each test case.
+ test_cases_[i]->UnshuffleTests();
+ // Resets the index of each test case.
+ test_case_indices_[i] = static_cast<int>(i);
+ }
+}
+
+// Returns the current OS stack trace as an std::string.
+//
+// The maximum number of stack frames to be included is specified by
+// the gtest_stack_trace_depth flag. The skip_count parameter
+// specifies the number of top frames to be skipped, which doesn't
+// count against the number of frames to be included.
+//
+// For example, if Foo() calls Bar(), which in turn calls
+// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in
+// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't.
+std::string GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/,
+ int skip_count) {
+ // We pass skip_count + 1 to skip this wrapper function in addition
+ // to what the user really wants to skip.
+ return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1);
+}
+
+// Used by the GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_ macro to
+// suppress unreachable code warnings.
+namespace {
+class ClassUniqueToAlwaysTrue {};
+}
+
+bool IsTrue(bool condition) { return condition; }
+
+bool AlwaysTrue() {
+#if GTEST_HAS_EXCEPTIONS
+ // This condition is always false so AlwaysTrue() never actually throws,
+ // but it makes the compiler think that it may throw.
+ if (IsTrue(false))
+ throw ClassUniqueToAlwaysTrue();
+#endif // GTEST_HAS_EXCEPTIONS
+ return true;
+}
+
+// If *pstr starts with the given prefix, modifies *pstr to be right
+// past the prefix and returns true; otherwise leaves *pstr unchanged
+// and returns false. None of pstr, *pstr, and prefix can be NULL.
+bool SkipPrefix(const char* prefix, const char** pstr) {
+ const size_t prefix_len = strlen(prefix);
+ if (strncmp(*pstr, prefix, prefix_len) == 0) {
+ *pstr += prefix_len;
+ return true;
+ }
+ return false;
+}
+
+// Parses a string as a command line flag. The string should have
+// the format "--flag=value". When def_optional is true, the "=value"
+// part can be omitted.
+//
+// Returns the value of the flag, or NULL if the parsing failed.
+const char* ParseFlagValue(const char* str,
+ const char* flag,
+ bool def_optional) {
+ // str and flag must not be NULL.
+ if (str == NULL || flag == NULL) return NULL;
+
+ // The flag must start with "--" followed by GTEST_FLAG_PREFIX_.
+ const std::string flag_str = std::string("--") + GTEST_FLAG_PREFIX_ + flag;
+ const size_t flag_len = flag_str.length();
+ if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL;
+
+ // Skips the flag name.
+ const char* flag_end = str + flag_len;
+
+ // When def_optional is true, it's OK to not have a "=value" part.
+ if (def_optional && (flag_end[0] == '\0')) {
+ return flag_end;
+ }
+
+ // If def_optional is true and there are more characters after the
+ // flag name, or if def_optional is false, there must be a '=' after
+ // the flag name.
+ if (flag_end[0] != '=') return NULL;
+
+ // Returns the string after "=".
+ return flag_end + 1;
+}
+
+// Parses a string for a bool flag, in the form of either
+// "--flag=value" or "--flag".
+//
+// In the former case, the value is taken as true as long as it does
+// not start with '0', 'f', or 'F'.
+//
+// In the latter case, the value is taken as true.
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+bool ParseBoolFlag(const char* str, const char* flag, bool* value) {
+ // Gets the value of the flag as a string.
+ const char* const value_str = ParseFlagValue(str, flag, true);
+
+ // Aborts if the parsing failed.
+ if (value_str == NULL) return false;
+
+ // Converts the string value to a bool.
+ *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F');
+ return true;
+}
+
+// Parses a string for an Int32 flag, in the form of
+// "--flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+bool ParseInt32Flag(const char* str, const char* flag, Int32* value) {
+ // Gets the value of the flag as a string.
+ const char* const value_str = ParseFlagValue(str, flag, false);
+
+ // Aborts if the parsing failed.
+ if (value_str == NULL) return false;
+
+ // Sets *value to the value of the flag.
+ return ParseInt32(Message() << "The value of flag --" << flag,
+ value_str, value);
+}
+
+// Parses a string for a string flag, in the form of
+// "--flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+bool ParseStringFlag(const char* str, const char* flag, std::string* value) {
+ // Gets the value of the flag as a string.
+ const char* const value_str = ParseFlagValue(str, flag, false);
+
+ // Aborts if the parsing failed.
+ if (value_str == NULL) return false;
+
+ // Sets *value to the value of the flag.
+ *value = value_str;
+ return true;
+}
+
+// Determines whether a string has a prefix that Google Test uses for its
+// flags, i.e., starts with GTEST_FLAG_PREFIX_ or GTEST_FLAG_PREFIX_DASH_.
+// If Google Test detects that a command line flag has its prefix but is not
+// recognized, it will print its help message. Flags starting with
+// GTEST_INTERNAL_PREFIX_ followed by "internal_" are considered Google Test
+// internal flags and do not trigger the help message.
+static bool HasGoogleTestFlagPrefix(const char* str) {
+ return (SkipPrefix("--", &str) ||
+ SkipPrefix("-", &str) ||
+ SkipPrefix("/", &str)) &&
+ !SkipPrefix(GTEST_FLAG_PREFIX_ "internal_", &str) &&
+ (SkipPrefix(GTEST_FLAG_PREFIX_, &str) ||
+ SkipPrefix(GTEST_FLAG_PREFIX_DASH_, &str));
+}
+
+// Prints a string containing code-encoded text. The following escape
+// sequences can be used in the string to control the text color:
+//
+// @@ prints a single '@' character.
+// @R changes the color to red.
+// @G changes the color to green.
+// @Y changes the color to yellow.
+// @D changes to the default terminal text color.
+//
+// TODO(wan@google.com): Write tests for this once we add stdout
+// capturing to Google Test.
+static void PrintColorEncoded(const char* str) {
+ GTestColor color = COLOR_DEFAULT; // The current color.
+
+ // Conceptually, we split the string into segments divided by escape
+ // sequences. Then we print one segment at a time. At the end of
+ // each iteration, the str pointer advances to the beginning of the
+ // next segment.
+ for (;;) {
+ const char* p = strchr(str, '@');
+ if (p == NULL) {
+ ColoredPrintf(color, "%s", str);
+ return;
+ }
+
+ ColoredPrintf(color, "%s", std::string(str, p).c_str());
+
+ const char ch = p[1];
+ str = p + 2;
+ if (ch == '@') {
+ ColoredPrintf(color, "@");
+ } else if (ch == 'D') {
+ color = COLOR_DEFAULT;
+ } else if (ch == 'R') {
+ color = COLOR_RED;
+ } else if (ch == 'G') {
+ color = COLOR_GREEN;
+ } else if (ch == 'Y') {
+ color = COLOR_YELLOW;
+ } else {
+ --str;
+ }
+ }
+}
+
+static const char kColorEncodedHelpMessage[] =
+"This program contains tests written using " GTEST_NAME_ ". You can use the\n"
+"following command line flags to control its behavior:\n"
+"\n"
+"Test Selection:\n"
+" @G--" GTEST_FLAG_PREFIX_ "list_tests@D\n"
+" List the names of all tests instead of running them. The name of\n"
+" TEST(Foo, Bar) is \"Foo.Bar\".\n"
+" @G--" GTEST_FLAG_PREFIX_ "filter=@YPOSTIVE_PATTERNS"
+ "[@G-@YNEGATIVE_PATTERNS]@D\n"
+" Run only the tests whose name matches one of the positive patterns but\n"
+" none of the negative patterns. '?' matches any single character; '*'\n"
+" matches any substring; ':' separates two patterns.\n"
+" @G--" GTEST_FLAG_PREFIX_ "also_run_disabled_tests@D\n"
+" Run all disabled tests too.\n"
+"\n"
+"Test Execution:\n"
+" @G--" GTEST_FLAG_PREFIX_ "repeat=@Y[COUNT]@D\n"
+" Run the tests repeatedly; use a negative count to repeat forever.\n"
+" @G--" GTEST_FLAG_PREFIX_ "shuffle@D\n"
+" Randomize tests' orders on every iteration.\n"
+" @G--" GTEST_FLAG_PREFIX_ "random_seed=@Y[NUMBER]@D\n"
+" Random number seed to use for shuffling test orders (between 1 and\n"
+" 99999, or 0 to use a seed based on the current time).\n"
+"\n"
+"Test Output:\n"
+" @G--" GTEST_FLAG_PREFIX_ "color=@Y(@Gyes@Y|@Gno@Y|@Gauto@Y)@D\n"
+" Enable/disable colored output. The default is @Gauto@D.\n"
+" -@G-" GTEST_FLAG_PREFIX_ "print_time=0@D\n"
+" Don't print the elapsed time of each test.\n"
+" @G--" GTEST_FLAG_PREFIX_ "output=xml@Y[@G:@YDIRECTORY_PATH@G"
+ GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n"
+" Generate an XML report in the given directory or with the given file\n"
+" name. @YFILE_PATH@D defaults to @Gtest_details.xml@D.\n"
+#if GTEST_CAN_STREAM_RESULTS_
+" @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@YHOST@G:@YPORT@D\n"
+" Stream test results to the given server.\n"
+#endif // GTEST_CAN_STREAM_RESULTS_
+"\n"
+"Assertion Behavior:\n"
+#if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS
+" @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n"
+" Set the default death test style.\n"
+#endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS
+" @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n"
+" Turn assertion failures into debugger break-points.\n"
+" @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n"
+" Turn assertion failures into C++ exceptions.\n"
+" @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0@D\n"
+" Do not report exceptions as test failures. Instead, allow them\n"
+" to crash the program or throw a pop-up (on Windows).\n"
+"\n"
+"Except for @G--" GTEST_FLAG_PREFIX_ "list_tests@D, you can alternatively set "
+ "the corresponding\n"
+"environment variable of a flag (all letters in upper-case). For example, to\n"
+"disable colored text output, you can either specify @G--" GTEST_FLAG_PREFIX_
+ "color=no@D or set\n"
+"the @G" GTEST_FLAG_PREFIX_UPPER_ "COLOR@D environment variable to @Gno@D.\n"
+"\n"
+"For more information, please read the " GTEST_NAME_ " documentation at\n"
+"@G" GTEST_PROJECT_URL_ "@D. If you find a bug in " GTEST_NAME_ "\n"
+"(not one in your own code or tests), please report it to\n"
+"@G<" GTEST_DEV_EMAIL_ ">@D.\n";
+
+// Parses the command line for Google Test flags, without initializing
+// other parts of Google Test. The type parameter CharType can be
+// instantiated to either char or wchar_t.
+template <typename CharType>
+void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) {
+ for (int i = 1; i < *argc; i++) {
+ const std::string arg_string = StreamableToString(argv[i]);
+ const char* const arg = arg_string.c_str();
+
+ using internal::ParseBoolFlag;
+ using internal::ParseInt32Flag;
+ using internal::ParseStringFlag;
+
+ // Do we see a Google Test flag?
+ if (ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag,
+ &GTEST_FLAG(also_run_disabled_tests)) ||
+ ParseBoolFlag(arg, kBreakOnFailureFlag,
+ &GTEST_FLAG(break_on_failure)) ||
+ ParseBoolFlag(arg, kCatchExceptionsFlag,
+ &GTEST_FLAG(catch_exceptions)) ||
+ ParseStringFlag(arg, kColorFlag, &GTEST_FLAG(color)) ||
+ ParseStringFlag(arg, kDeathTestStyleFlag,
+ &GTEST_FLAG(death_test_style)) ||
+ ParseBoolFlag(arg, kDeathTestUseFork,
+ &GTEST_FLAG(death_test_use_fork)) ||
+ ParseStringFlag(arg, kFilterFlag, &GTEST_FLAG(filter)) ||
+ ParseStringFlag(arg, kInternalRunDeathTestFlag,
+ &GTEST_FLAG(internal_run_death_test)) ||
+ ParseBoolFlag(arg, kListTestsFlag, &GTEST_FLAG(list_tests)) ||
+ ParseStringFlag(arg, kOutputFlag, &GTEST_FLAG(output)) ||
+ ParseBoolFlag(arg, kPrintTimeFlag, &GTEST_FLAG(print_time)) ||
+ ParseInt32Flag(arg, kRandomSeedFlag, &GTEST_FLAG(random_seed)) ||
+ ParseInt32Flag(arg, kRepeatFlag, &GTEST_FLAG(repeat)) ||
+ ParseBoolFlag(arg, kShuffleFlag, &GTEST_FLAG(shuffle)) ||
+ ParseInt32Flag(arg, kStackTraceDepthFlag,
+ &GTEST_FLAG(stack_trace_depth)) ||
+ ParseStringFlag(arg, kStreamResultToFlag,
+ &GTEST_FLAG(stream_result_to)) ||
+ ParseBoolFlag(arg, kThrowOnFailureFlag,
+ &GTEST_FLAG(throw_on_failure))
+ ) {
+ // Yes. Shift the remainder of the argv list left by one. Note
+ // that argv has (*argc + 1) elements, the last one always being
+ // NULL. The following loop moves the trailing NULL element as
+ // well.
+ for (int j = i; j != *argc; j++) {
+ argv[j] = argv[j + 1];
+ }
+
+ // Decrements the argument count.
+ (*argc)--;
+
+ // We also need to decrement the iterator as we just removed
+ // an element.
+ i--;
+ } else if (arg_string == "--help" || arg_string == "-h" ||
+ arg_string == "-?" || arg_string == "/?" ||
+ HasGoogleTestFlagPrefix(arg)) {
+ // Both help flag and unrecognized Google Test flags (excluding
+ // internal ones) trigger help display.
+ g_help_flag = true;
+ }
+ }
+
+ if (g_help_flag) {
+ // We print the help here instead of in RUN_ALL_TESTS(), as the
+ // latter may not be called at all if the user is using Google
+ // Test with another testing framework.
+ PrintColorEncoded(kColorEncodedHelpMessage);
+ }
+}
+
+// Parses the command line for Google Test flags, without initializing
+// other parts of Google Test.
+void ParseGoogleTestFlagsOnly(int* argc, char** argv) {
+ ParseGoogleTestFlagsOnlyImpl(argc, argv);
+}
+void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) {
+ ParseGoogleTestFlagsOnlyImpl(argc, argv);
+}
+
+// The internal implementation of InitGoogleTest().
+//
+// The type parameter CharType can be instantiated to either char or
+// wchar_t.
+template <typename CharType>
+void InitGoogleTestImpl(int* argc, CharType** argv) {
+ g_init_gtest_count++;
+
+ // We don't want to run the initialization code twice.
+ if (g_init_gtest_count != 1) return;
+
+ if (*argc <= 0) return;
+
+ internal::g_executable_path = internal::StreamableToString(argv[0]);
+
+#if GTEST_HAS_DEATH_TEST
+
+ g_argvs.clear();
+ for (int i = 0; i != *argc; i++) {
+ g_argvs.push_back(StreamableToString(argv[i]));
+ }
+
+#endif // GTEST_HAS_DEATH_TEST
+
+ ParseGoogleTestFlagsOnly(argc, argv);
+ GetUnitTestImpl()->PostFlagParsingInit();
+}
+
+} // namespace internal
+
+// Initializes Google Test. This must be called before calling
+// RUN_ALL_TESTS(). In particular, it parses a command line for the
+// flags that Google Test recognizes. Whenever a Google Test flag is
+// seen, it is removed from argv, and *argc is decremented.
+//
+// No value is returned. Instead, the Google Test flag variables are
+// updated.
+//
+// Calling the function for the second time has no user-visible effect.
+void InitGoogleTest(int* argc, char** argv) {
+ internal::InitGoogleTestImpl(argc, argv);
+}
+
+// This overloaded version can be used in Windows programs compiled in
+// UNICODE mode.
+void InitGoogleTest(int* argc, wchar_t** argv) {
+ internal::InitGoogleTestImpl(argc, argv);
+}
+
+} // namespace testing
diff --git a/extern/gtest/src/gtest_main.cc b/extern/gtest/src/gtest_main.cc
new file mode 100644
index 00000000000..f3028225523
--- /dev/null
+++ b/extern/gtest/src/gtest_main.cc
@@ -0,0 +1,38 @@
+// Copyright 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdio.h>
+
+#include "gtest/gtest.h"
+
+GTEST_API_ int main(int argc, char **argv) {
+ printf("Running main() from gtest_main.cc\n");
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/extern/libmv/CMakeLists.txt b/extern/libmv/CMakeLists.txt
index c3c5143cc16..e1595ce08a0 100644
--- a/extern/libmv/CMakeLists.txt
+++ b/extern/libmv/CMakeLists.txt
@@ -30,6 +30,9 @@ set(INC
.
)
+set(INC_SYS
+)
+
set(SRC
libmv-capi.h
libmv-capi_intern.h
@@ -67,6 +70,7 @@ if(WITH_LIBMV)
libmv/multiview/fundamental.cc
libmv/multiview/homography.cc
libmv/multiview/panography.cc
+ libmv/multiview/panography_kernel.cc
libmv/multiview/projection.cc
libmv/multiview/triangulation.cc
libmv/numeric/numeric.cc
@@ -92,10 +96,6 @@ if(WITH_LIBMV)
libmv/tracking/track_region.cc
libmv/tracking/trklt_region_tracker.cc
- third_party/gflags/gflags.cc
- third_party/gflags/gflags_completions.cc
- third_party/gflags/gflags_reporting.cc
-
libmv-util.h
libmv/base/aligned_malloc.h
libmv/base/id_generator.h
@@ -106,6 +106,7 @@ if(WITH_LIBMV)
libmv/image/convolve.h
libmv/image/correlation.h
libmv/image/image_converter.h
+ libmv/image/image_drawing.h
libmv/image/image.h
libmv/image/sample.h
libmv/image/tuple.h
@@ -113,13 +114,16 @@ if(WITH_LIBMV)
libmv/multiview/conditioning.h
libmv/multiview/euclidean_resection.h
libmv/multiview/fundamental.h
+ libmv/multiview/homography_error.h
libmv/multiview/homography.h
libmv/multiview/homography_parameterization.h
libmv/multiview/nviewtriangulation.h
libmv/multiview/panography.h
+ libmv/multiview/panography_kernel.h
libmv/multiview/projection.h
libmv/multiview/resection.h
libmv/multiview/triangulation.h
+ libmv/multiview/two_view_kernel.h
libmv/numeric/dogleg.h
libmv/numeric/function_derivative.h
libmv/numeric/levenberg_marquardt.h
@@ -149,18 +153,86 @@ if(WITH_LIBMV)
libmv/tracking/track_region.h
libmv/tracking/trklt_region_tracker.h
+ third_party/msinttypes/inttypes.h
+ third_party/msinttypes/stdint.h
+ )
+
+ if(WIN32)
+ list(APPEND INC
+ third_party/glog/src/windows
+ )
+
+ if(NOT MINGW)
+ list(APPEND INC
+ third_party/msinttypes
+ )
+ endif()
+ endif()
+
+ if(WITH_GTESTS)
+ blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "" "")
+
+ BLENDER_SRC_GTEST("libmv_scoped_ptr" "./libmv/base/scoped_ptr_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_vector" "./libmv/base/vector_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_array_nd" "./libmv/image/array_nd_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_convolve" "./libmv/image/convolve_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_image" "./libmv/image/image_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_sample" "./libmv/image/sample_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_tuple" "./libmv/image/tuple_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_euclidean_resection" "./libmv/multiview/euclidean_resection_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_fundamental" "./libmv/multiview/fundamental_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_homography" "./libmv/multiview/homography_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_nviewtriangulation" "./libmv/multiview/nviewtriangulation_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_panography" "./libmv/multiview/panography_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_projection" "./libmv/multiview/projection_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_resection" "./libmv/multiview/resection_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_triangulation" "./libmv/multiview/triangulation_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_dogleg" "./libmv/numeric/dogleg_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_function_derivative" "./libmv/numeric/function_derivative_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_levenberg_marquardt" "./libmv/numeric/levenberg_marquardt_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_numeric" "./libmv/numeric/numeric_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_poly" "./libmv/numeric/poly_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_camera_intrinsics" "./libmv/simple_pipeline/camera_intrinsics_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_detect" "./libmv/simple_pipeline/detect_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_intersect" "./libmv/simple_pipeline/intersect_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_keyframe_selection" "./libmv/simple_pipeline/keyframe_selection_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_modal_solver" "./libmv/simple_pipeline/modal_solver_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_resect" "./libmv/simple_pipeline/resect_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_brute_region_tracker" "./libmv/tracking/brute_region_tracker_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_klt_region_tracker" "./libmv/tracking/klt_region_tracker_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ BLENDER_SRC_GTEST("libmv_pyramid_region_tracker" "./libmv/tracking/pyramid_region_tracker_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
+ endif()
+else()
+ list(APPEND SRC
+ libmv-capi_stub.cc
+ )
+endif()
+
+blender_add_lib(extern_libmv "${SRC}" "${INC}" "${INC_SYS}")
+
+if(WITH_LIBMV)
+ add_subdirectory(third_party)
+endif()
+
+# make GLog a separate target, so it can be used for gtest as well.
+if(WITH_LIBMV OR WITH_GTESTS)
+ # We compile GLog together with GFlag so we don't worry about
+ # adding extra lib to linker.
+ set(GLOG_SRC
+ third_party/gflags/gflags.cc
+ third_party/gflags/gflags_completions.cc
+ third_party/gflags/gflags_reporting.cc
+
third_party/gflags/config.h
third_party/gflags/gflags/gflags_completions.h
third_party/gflags/gflags/gflags_declare.h
third_party/gflags/gflags/gflags.h
third_party/gflags/mutex.h
third_party/gflags/util.h
- third_party/msinttypes/inttypes.h
- third_party/msinttypes/stdint.h
)
if(WIN32)
- list(APPEND SRC
+ list(APPEND GLOG_SRC
third_party/glog/src/logging.cc
third_party/glog/src/raw_logging.cc
third_party/glog/src/utilities.cc
@@ -185,18 +257,8 @@ if(WITH_LIBMV)
third_party/glog/src/windows/port.h
third_party/glog/src/windows/config.h
)
-
- list(APPEND INC
- third_party/glog/src/windows
- )
-
- if(NOT MINGW)
- list(APPEND INC
- third_party/msinttypes
- )
- endif()
else()
- list(APPEND SRC
+ list(APPEND GLOG_SRC
third_party/glog/src/demangle.cc
third_party/glog/src/logging.cc
third_party/glog/src/raw_logging.cc
@@ -228,14 +290,14 @@ if(WITH_LIBMV)
third_party/glog/src/utilities.h
)
endif()
-else()
- list(APPEND SRC
- libmv-capi_stub.cc
+
+ set(GLOG_INC
+ third_party/gflags
+ third_party/glog/src
)
-endif()
-blender_add_lib(extern_libmv "${SRC}" "${INC}" "${INC_SYS}")
+ set(GLOG_INC_SYS
+ )
-if(WITH_LIBMV)
- add_subdirectory(third_party)
+ blender_add_lib(extern_glog "${GLOG_SRC}" "${GLOG_INC}" "${GLOG_INC_SYS}")
endif()
diff --git a/extern/libmv/SConscript b/extern/libmv/SConscript
index dc129501847..a267c96520e 100644
--- a/extern/libmv/SConscript
+++ b/extern/libmv/SConscript
@@ -44,6 +44,8 @@ if env['WITH_BF_LIBMV']:
else:
src = env.Glob("libmv-capi_stub.cc")
+src = [src for src in src if src.find('_test.cc') == -1]
+
env.BlenderLib ( libname = 'extern_libmv', sources=src, includes=Split(incs), defines=defs, libtype=['extern', 'player'], priority=[20,137] )
if env['WITH_BF_LIBMV']:
diff --git a/extern/libmv/bundle.sh b/extern/libmv/bundle.sh
index e8e698a89f2..32f7311ca96 100755
--- a/extern/libmv/bundle.sh
+++ b/extern/libmv/bundle.sh
@@ -30,15 +30,20 @@ rm -rf $tmp
chmod 664 ./third_party/glog/src/windows/*.cc ./third_party/glog/src/windows/*.h ./third_party/glog/src/windows/glog/*.h
-sources=`find ./libmv -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | sed -r 's/^\.\//\t\t/' | sort -d`
-headers=`find ./libmv -type f -iname '*.h' | sed -r 's/^\.\//\t\t/' | sort -d`
+sources=`find ./libmv -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep -v _test.cc | grep -v test_data_sets | sed -r 's/^\.\//\t\t/' | sort -d`
+headers=`find ./libmv -type f -iname '*.h' | grep -v test_data_sets | sed -r 's/^\.\//\t\t/' | sort -d`
-third_sources=`find ./third_party -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep -v glog | grep -v ceres | sed -r 's/^\.\//\t\t/' | sort -d`
-third_headers=`find ./third_party -type f -iname '*.h' | grep -v glog | grep -v ceres | sed -r 's/^\.\//\t\t/' | sort -d`
+third_sources=`find ./third_party -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep -v glog | grep -v gflags | grep -v ceres | sed -r 's/^\.\//\t\t/' | sort -d`
+third_headers=`find ./third_party -type f -iname '*.h' | grep -v glog | grep -v gflags | grep -v ceres | sed -r 's/^\.\//\t\t/' | sort -d`
third_glog_sources=`find ./third_party -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep glog | grep -v windows | sed -r 's/^\.\//\t\t\t/' | sort -d`
third_glog_headers=`find ./third_party -type f -iname '*.h' | grep glog | grep -v windows | sed -r 's/^\.\//\t\t\t/' | sort -d`
+third_gflags_sources=`find ./third_party -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep gflags | grep -v windows | sed -r 's/^\.\//\t\t/' | sort -d`
+third_gflags_headers=`find ./third_party -type f -iname '*.h' | grep gflags | grep -v windows | sed -r 's/^\.\//\t\t/' | sort -d`
+
+tests=`find ./libmv -type f -iname '*_test.cc' | sort -d | awk ' { name=gensub(".*/([A-Za-z_]+)_test.cc", "\\\\1", $1); printf("\t\tBLENDER_SRC_GTEST(\"libmv_%s\" \"%s\" \"libmv_test_dataset;extern_libmv;extern_ceres\")\n", name, $1) } '`
+
src_dir=`find ./libmv -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | sed -r 's/^\.\//\t\t/' | sort -d | uniq`
src_third_dir=`find ./third_party -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | grep -v ceres | sed -r 's/^\.\//\t\t/' | sort -d | uniq`
src=""
@@ -118,6 +123,9 @@ set(INC
.
)
+set(INC_SYS
+)
+
set(SRC
libmv-capi.h
libmv-capi_intern.h
@@ -148,9 +156,7 @@ if(WITH_LIBMV)
libmv-capi.cc
libmv-util.cc
${sources}
-
${third_sources}
-
libmv-util.h
${headers}
@@ -158,7 +164,46 @@ ${third_headers}
)
if(WIN32)
- list(APPEND SRC
+ list(APPEND INC
+ third_party/glog/src/windows
+ )
+
+ if(NOT MINGW)
+ list(APPEND INC
+ third_party/msinttypes
+ )
+ endif()
+ endif()
+
+ if(WITH_GTESTS)
+ blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "${INC}" "${INC_SYS}")
+
+${tests}
+ endif()
+else()
+ list(APPEND SRC
+ libmv-capi_stub.cc
+ )
+endif()
+
+blender_add_lib(extern_libmv "\${SRC}" "\${INC}" "\${INC_SYS}")
+
+if(WITH_LIBMV)
+ add_subdirectory(third_party)
+endif()
+
+# make GLog a separate target, so it can be used for gtest as well.
+if(WITH_LIBMV OR WITH_GTESTS)
+ # We compile GLog together with GFlag so we don't worry about
+ # adding extra lib to linker.
+ set(GLOG_SRC
+${third_gflags_sources}
+
+${third_gflags_headers}
+ )
+
+ if(WIN32)
+ list(APPEND GLOG_SRC
third_party/glog/src/logging.cc
third_party/glog/src/raw_logging.cc
third_party/glog/src/utilities.cc
@@ -183,33 +228,23 @@ ${third_headers}
third_party/glog/src/windows/port.h
third_party/glog/src/windows/config.h
)
-
- list(APPEND INC
- third_party/glog/src/windows
- )
-
- if(NOT MINGW)
- list(APPEND INC
- third_party/msinttypes
- )
- endif()
else()
- list(APPEND SRC
+ list(APPEND GLOG_SRC
${third_glog_sources}
${third_glog_headers}
)
endif()
-else()
- list(APPEND SRC
- libmv-capi_stub.cc
+
+ set(GLOG_INC
+ third_party/gflags
+ third_party/glog/src
)
-endif()
-blender_add_lib(extern_libmv "\${SRC}" "\${INC}" "\${INC_SYS}")
+ set(GLOG_INC_SYS
+ )
-if(WITH_LIBMV)
- add_subdirectory(third_party)
+ blender_add_lib(extern_glog "\${GLOG_SRC}" "\${GLOG_INC}" "\${GLOG_INC_SYS}")
endif()
EOF
@@ -254,6 +289,8 @@ ${win_src}
else:
src = env.Glob("libmv-capi_stub.cc")
+src = [src for src in src if src.find('_test.cc') == -1]
+
env.BlenderLib ( libname = 'extern_libmv', sources=src, includes=Split(incs), defines=defs, libtype=['extern', 'player'], priority=[20,137] )
if env['WITH_BF_LIBMV']:
diff --git a/extern/libmv/files.txt b/extern/libmv/files.txt
index 60a99307835..f1eb9580ff5 100644
--- a/extern/libmv/files.txt
+++ b/extern/libmv/files.txt
@@ -2,60 +2,92 @@ libmv/base/aligned_malloc.cc
libmv/base/aligned_malloc.h
libmv/base/id_generator.h
libmv/base/scoped_ptr.h
+libmv/base/scoped_ptr_test.cc
libmv/base/vector.h
+libmv/base/vector_test.cc
libmv/base/vector_utils.h
libmv/image/array_nd.cc
libmv/image/array_nd.h
+libmv/image/array_nd_test.cc
libmv/image/convolve.cc
libmv/image/convolve.h
+libmv/image/convolve_test.cc
libmv/image/correlation.h
libmv/image/image_converter.h
+libmv/image/image_drawing.h
libmv/image/image.h
+libmv/image/image_test.cc
libmv/image/sample.h
+libmv/image/sample_test.cc
libmv/image/tuple.h
+libmv/image/tuple_test.cc
libmv/logging/logging.h
libmv/multiview/conditioning.cc
libmv/multiview/conditioning.h
libmv/multiview/euclidean_resection.cc
libmv/multiview/euclidean_resection.h
+libmv/multiview/euclidean_resection_test.cc
libmv/multiview/fundamental.cc
libmv/multiview/fundamental.h
+libmv/multiview/fundamental_test.cc
libmv/multiview/homography.cc
+libmv/multiview/homography_error.h
libmv/multiview/homography.h
libmv/multiview/homography_parameterization.h
+libmv/multiview/homography_test.cc
libmv/multiview/nviewtriangulation.h
+libmv/multiview/nviewtriangulation_test.cc
libmv/multiview/panography.cc
libmv/multiview/panography.h
+libmv/multiview/panography_kernel.cc
+libmv/multiview/panography_kernel.h
+libmv/multiview/panography_test.cc
libmv/multiview/projection.cc
libmv/multiview/projection.h
+libmv/multiview/projection_test.cc
libmv/multiview/resection.h
+libmv/multiview/resection_test.cc
+libmv/multiview/test_data_sets.cc
+libmv/multiview/test_data_sets.h
libmv/multiview/triangulation.cc
libmv/multiview/triangulation.h
+libmv/multiview/triangulation_test.cc
+libmv/multiview/two_view_kernel.h
libmv/numeric/dogleg.h
+libmv/numeric/dogleg_test.cc
libmv/numeric/function_derivative.h
+libmv/numeric/function_derivative_test.cc
libmv/numeric/levenberg_marquardt.h
+libmv/numeric/levenberg_marquardt_test.cc
libmv/numeric/numeric.cc
libmv/numeric/numeric.h
+libmv/numeric/numeric_test.cc
libmv/numeric/poly.cc
libmv/numeric/poly.h
+libmv/numeric/poly_test.cc
libmv/simple_pipeline/bundle.cc
libmv/simple_pipeline/bundle.h
libmv/simple_pipeline/callbacks.h
libmv/simple_pipeline/camera_intrinsics.cc
libmv/simple_pipeline/camera_intrinsics.h
libmv/simple_pipeline/camera_intrinsics_impl.h
+libmv/simple_pipeline/camera_intrinsics_test.cc
libmv/simple_pipeline/detect.cc
libmv/simple_pipeline/detect.h
+libmv/simple_pipeline/detect_test.cc
libmv/simple_pipeline/distortion_models.cc
libmv/simple_pipeline/distortion_models.h
libmv/simple_pipeline/initialize_reconstruction.cc
libmv/simple_pipeline/initialize_reconstruction.h
libmv/simple_pipeline/intersect.cc
libmv/simple_pipeline/intersect.h
+libmv/simple_pipeline/intersect_test.cc
libmv/simple_pipeline/keyframe_selection.cc
libmv/simple_pipeline/keyframe_selection.h
+libmv/simple_pipeline/keyframe_selection_test.cc
libmv/simple_pipeline/modal_solver.cc
libmv/simple_pipeline/modal_solver.h
+libmv/simple_pipeline/modal_solver_test.cc
libmv/simple_pipeline/pipeline.cc
libmv/simple_pipeline/pipeline.h
libmv/simple_pipeline/reconstruction.cc
@@ -64,16 +96,20 @@ libmv/simple_pipeline/reconstruction_scale.cc
libmv/simple_pipeline/reconstruction_scale.h
libmv/simple_pipeline/resect.cc
libmv/simple_pipeline/resect.h
+libmv/simple_pipeline/resect_test.cc
libmv/simple_pipeline/tracks.cc
libmv/simple_pipeline/tracks.h
libmv/tracking/brute_region_tracker.cc
libmv/tracking/brute_region_tracker.h
+libmv/tracking/brute_region_tracker_test.cc
libmv/tracking/hybrid_region_tracker.cc
libmv/tracking/hybrid_region_tracker.h
libmv/tracking/klt_region_tracker.cc
libmv/tracking/klt_region_tracker.h
+libmv/tracking/klt_region_tracker_test.cc
libmv/tracking/pyramid_region_tracker.cc
libmv/tracking/pyramid_region_tracker.h
+libmv/tracking/pyramid_region_tracker_test.cc
libmv/tracking/region_tracker.h
libmv/tracking/retrack_region_tracker.cc
libmv/tracking/retrack_region_tracker.h
diff --git a/extern/libmv/libmv/base/scoped_ptr_test.cc b/extern/libmv/libmv/base/scoped_ptr_test.cc
new file mode 100644
index 00000000000..ce1d56b500a
--- /dev/null
+++ b/extern/libmv/libmv/base/scoped_ptr_test.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2009 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/base/scoped_ptr.h"
+#include "testing/testing.h"
+
+namespace libmv {
+namespace {
+
+struct FreeMe {
+ FreeMe(int *freed) : freed(freed) {}
+ ~FreeMe() { (*freed)++; }
+ int *freed;
+};
+
+TEST(ScopedPtr, NullDoesNothing) {
+ scoped_ptr<FreeMe> x(NULL);
+ // Does nothing.
+}
+
+TEST(ScopedPtr, FreesWhenOutOfScope) {
+ int frees = 0;
+ {
+ scoped_ptr<FreeMe> scoped(new FreeMe(&frees));
+ EXPECT_EQ(0, frees);
+ }
+ EXPECT_EQ(1, frees);
+}
+
+TEST(ScopedPtr, Operators) {
+ int tag = 123;
+ scoped_ptr<FreeMe> scoped(new FreeMe(&tag));
+ EXPECT_EQ(123, *(scoped->freed));
+ EXPECT_EQ(123, *((*scoped).freed));
+}
+
+TEST(ScopedPtr, Reset) {
+ int frees = 0;
+ scoped_ptr<FreeMe> scoped(new FreeMe(&frees));
+ EXPECT_EQ(0, frees);
+ scoped.reset(new FreeMe(&frees));
+ EXPECT_EQ(1, frees);
+}
+
+TEST(ScopedPtr, ReleaseAndGet) {
+ int frees = 0;
+ FreeMe *allocated = new FreeMe(&frees);
+ FreeMe *released = NULL;
+ {
+ scoped_ptr<FreeMe> scoped(allocated);
+ EXPECT_EQ(0, frees);
+ EXPECT_EQ(allocated, scoped.get());
+ released = scoped.release();
+ EXPECT_EQ(0, frees);
+ EXPECT_EQ(released, allocated);
+ }
+ EXPECT_EQ(0, frees);
+ delete released;
+}
+
+} // namespace
+} // namespace libmv
diff --git a/extern/libmv/libmv/base/vector_test.cc b/extern/libmv/libmv/base/vector_test.cc
new file mode 100644
index 00000000000..f17718c3926
--- /dev/null
+++ b/extern/libmv/libmv/base/vector_test.cc
@@ -0,0 +1,223 @@
+// Copyright (c) 2009 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/base/vector.h"
+#include "libmv/numeric/numeric.h"
+#include "testing/testing.h"
+#include <algorithm>
+
+namespace {
+using namespace libmv;
+
+// This uses a Vec2d which is a fixed-size vectorizable Eigen type. It is
+// necessary to test vectorizable types to ensure that the alignment asserts
+// trigger if the alignment is not correct.
+TEST(VectorAlignmentTest, PushBack) {
+ Vec2 x1, x2;
+ x1 << 1, 2;
+ x2 << 3, 4;
+
+ vector<Vec2> vs;
+ vs.push_back(x1);
+ EXPECT_EQ(1, vs.size());
+ EXPECT_EQ(1, vs.capacity());
+
+ vs.push_back(x2);
+ EXPECT_EQ(2, vs.size());
+ EXPECT_EQ(2, vs.capacity());
+
+ // The following is necessary because of some bug in gtest; the expected
+ // parameter can't be a fixed size vectorizable type with alignment
+ // requirements.
+ Vec x1r = x1;
+ Vec x2r = x2;
+ EXPECT_EQ(x1r, vs[0]);
+ EXPECT_EQ(x2r, vs[1]);
+
+ vs.push_back(x2);
+ vs.push_back(x2);
+ vs.push_back(x2);
+ EXPECT_EQ(5, vs.size());
+ EXPECT_EQ(8, vs.capacity());
+}
+
+// Count the number of destruct calls to test that the destructor gets called.
+int foo_construct_calls = 0;
+int foo_destruct_calls = 0;
+
+struct Foo {
+ public:
+ Foo() : value(5) { foo_construct_calls++; }
+ ~Foo() { foo_destruct_calls++; }
+ int value;
+};
+
+struct VectorTest : public testing::Test {
+ VectorTest() {
+ foo_construct_calls = 0;
+ foo_destruct_calls = 0;
+ }
+};
+
+TEST_F(VectorTest, EmptyVectorDoesNotConstruct) {
+ {
+ vector<Foo> v;
+ EXPECT_EQ(0, v.size());
+ EXPECT_EQ(0, v.capacity());
+ }
+ EXPECT_EQ(0, foo_construct_calls);
+ EXPECT_EQ(0, foo_destruct_calls);
+}
+
+TEST_F(VectorTest, DestructorGetsCalled) {
+ {
+ vector<Foo> v;
+ v.resize(5);
+ }
+ EXPECT_EQ(5, foo_construct_calls);
+ EXPECT_EQ(5, foo_destruct_calls);
+}
+
+TEST_F(VectorTest, ReserveDoesNotCallConstructorsOrDestructors) {
+ vector<Foo> v;
+ EXPECT_EQ(0, v.size());
+ EXPECT_EQ(0, v.capacity());
+ EXPECT_EQ(0, foo_construct_calls);
+ EXPECT_EQ(0, foo_destruct_calls);
+
+ v.reserve(5);
+ EXPECT_EQ(0, v.size());
+ EXPECT_EQ(5, v.capacity());
+ EXPECT_EQ(0, foo_construct_calls);
+ EXPECT_EQ(0, foo_destruct_calls);
+}
+
+TEST_F(VectorTest, ResizeConstructsAndDestructsAsExpected) {
+ vector<Foo> v;
+
+ // Create one object.
+ v.resize(1);
+ EXPECT_EQ(1, v.size());
+ EXPECT_EQ(1, v.capacity());
+ EXPECT_EQ(1, foo_construct_calls);
+ EXPECT_EQ(0, foo_destruct_calls);
+ EXPECT_EQ(5, v[0].value);
+
+ // Create two more.
+ v.resize(3);
+ EXPECT_EQ(3, v.size());
+ EXPECT_EQ(3, v.capacity());
+ EXPECT_EQ(3, foo_construct_calls);
+ EXPECT_EQ(0, foo_destruct_calls);
+
+ // Delete the last one.
+ v.resize(2);
+ EXPECT_EQ(2, v.size());
+ EXPECT_EQ(3, v.capacity());
+ EXPECT_EQ(3, foo_construct_calls);
+ EXPECT_EQ(1, foo_destruct_calls);
+
+ // Delete the remaining two.
+ v.resize(0);
+ EXPECT_EQ(0, v.size());
+ EXPECT_EQ(3, v.capacity());
+ EXPECT_EQ(3, foo_construct_calls);
+ EXPECT_EQ(3, foo_destruct_calls);
+}
+
+TEST_F(VectorTest, PushPopBack) {
+ vector<Foo> v;
+
+ Foo foo;
+ foo.value = 10;
+ v.push_back(foo);
+ EXPECT_EQ(1, v.size());
+ EXPECT_EQ(10, v.back().value);
+
+ v.pop_back();
+ EXPECT_EQ(0, v.size());
+ EXPECT_EQ(1, foo_construct_calls);
+ EXPECT_EQ(1, foo_destruct_calls);
+}
+
+TEST_F(VectorTest, CopyConstructor) {
+ vector<int> a;
+ a.push_back(1);
+ a.push_back(5);
+ a.push_back(3);
+
+ vector<int> b(a);
+ EXPECT_EQ(a.size(), b.size());
+ //EXPECT_EQ(a.capacity(), b.capacity());
+ for (int i = 0; i < a.size(); ++i) {
+ EXPECT_EQ(a[i], b[i]);
+ }
+}
+
+TEST_F(VectorTest, OperatorEquals) {
+ vector<int> a, b;
+ a.push_back(1);
+ a.push_back(5);
+ a.push_back(3);
+
+ b = a;
+
+ EXPECT_EQ(a.size(), b.size());
+ //EXPECT_EQ(a.capacity(), b.capacity());
+ for (int i = 0; i < a.size(); ++i) {
+ EXPECT_EQ(a[i], b[i]);
+ }
+}
+
+TEST_F(VectorTest, STLFind) {
+ vector<int> a;
+ a.push_back(1);
+ a.push_back(5);
+ a.push_back(3);
+
+ // Find return an int *
+ EXPECT_EQ(std::find(&a[0], &a[2], 1) == &a[0], true);
+ EXPECT_EQ(std::find(&a[0], &a[2], 5) == &a[1], true);
+ EXPECT_EQ(std::find(&a[0], &a[2], 3) == &a[2], true);
+
+ // Find return a const int *
+ EXPECT_EQ(std::find(a.begin(), a.end(), 1) == &a[0], true);
+ EXPECT_EQ(std::find(a.begin(), a.end(), 5) == &a[1], true);
+ EXPECT_EQ(std::find(a.begin(), a.end(), 3) == &a[2], true);
+
+ // Search value that are not in the vector
+ EXPECT_EQ(std::find(a.begin(), a.end(), 0) == a.end(), true);
+ EXPECT_EQ(std::find(a.begin(), a.end(), 52) == a.end(), true);
+}
+
+TEST(Vector, swap) {
+ vector<int> a, b;
+ a.push_back(1);
+ a.push_back(2);
+ b.push_back(3);
+ a.swap(b);
+ EXPECT_EQ(1, a.size());
+ EXPECT_EQ(3, a[0]);
+ EXPECT_EQ(2, b.size());
+ EXPECT_EQ(1, b[0]);
+ EXPECT_EQ(2, b[1]);
+}
+
+} // namespace
diff --git a/extern/libmv/libmv/image/array_nd_test.cc b/extern/libmv/libmv/image/array_nd_test.cc
new file mode 100644
index 00000000000..313f21b60e9
--- /dev/null
+++ b/extern/libmv/libmv/image/array_nd_test.cc
@@ -0,0 +1,324 @@
+// Copyright (c) 2007, 2008 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/image/array_nd.h"
+#include "testing/testing.h"
+
+using libmv::ArrayND;
+using libmv::Array3D;
+using libmv::Array3Df;
+
+namespace {
+
+TEST(ArrayND, EmptyConstructor) {
+ ArrayND<int, 2> a;
+
+ EXPECT_EQ(0, a.Shape(0));
+ EXPECT_EQ(0, a.Shape(1));
+}
+
+TEST(ArrayND, IndexConstructor) {
+ int s[] = {1, 2, 3};
+ ArrayND<int, 3>::Index shape(s);
+ ArrayND<int, 3> a(shape);
+
+ EXPECT_EQ(1, a.Shape(0));
+ EXPECT_EQ(2, a.Shape(1));
+ EXPECT_EQ(3, a.Shape(2));
+}
+
+TEST(ArrayND, PointerConstructor) {
+ int s[] = {1, 2, 3};
+ ArrayND<int, 3> a(s);
+
+ EXPECT_EQ(1, a.Shape(0));
+ EXPECT_EQ(2, a.Shape(1));
+ EXPECT_EQ(3, a.Shape(2));
+}
+
+TEST(ArrayND, CopyConstructor) {
+ int s[] = {1, 2, 3};
+ ArrayND<int, 3> a(s);
+ a(0, 1, 1) = 3;
+ a(0, 1, 2) = 3;
+ ArrayND<int, 3> b(a);
+ EXPECT_EQ(1, b.Shape(0));
+ EXPECT_EQ(2, b.Shape(1));
+ EXPECT_EQ(3, b.Shape(2));
+ EXPECT_EQ(3, b(0, 1, 1));
+ b(0, 1, 2) = 2;
+ EXPECT_EQ(3, a(0, 1, 2));
+}
+
+TEST(ArrayND, Assignation) {
+ int s[] = {1, 2, 3};
+ ArrayND<int, 3> a(s);
+ a(0, 1, 1) = 3;
+ a(0, 1, 2) = 3;
+ ArrayND<int, 3> b;
+ b = a;
+ EXPECT_EQ(1, b.Shape(0));
+ EXPECT_EQ(2, b.Shape(1));
+ EXPECT_EQ(3, b.Shape(2));
+ EXPECT_EQ(3, b(0, 1, 1));
+ b(0, 1, 2) = 2;
+ EXPECT_EQ(3, a(0, 1, 2));
+}
+
+TEST(ArrayND, Fill) {
+ int s[] = {2, 2};
+ ArrayND<int, 2> a(s);
+ a.Fill(42);
+ EXPECT_EQ(42, a(0, 0));
+ EXPECT_EQ(42, a(0, 1));
+ EXPECT_EQ(42, a(1, 0));
+ EXPECT_EQ(42, a(1, 1));
+}
+
+TEST(ArrayND, Size) {
+ int s[] = {1, 2, 3};
+ ArrayND<int, 3>::Index shape(s);
+ ArrayND<int, 3> a(shape);
+
+ int l[] = {0, 1, 2};
+ ArrayND<int, 3>::Index last(l);
+
+ EXPECT_EQ(a.Size(), a.Offset(last)+1);
+ EXPECT_TRUE(a.Contains(last));
+ EXPECT_FALSE(a.Contains(shape));
+}
+
+TEST(ArrayND, MemorySizeInBytes) {
+ int s[] = {2, 3};
+ ArrayND<int, 2>::Index shape(s);
+ ArrayND<int, 2> a(shape);
+
+ int size = 24 + sizeof(a);
+ EXPECT_EQ(size, a.MemorySizeInBytes());
+}
+
+TEST(ArrayND, Parenthesis) {
+ typedef ArrayND<int, 2>::Index Index;
+
+ int s[] = {3, 3};
+ ArrayND<int, 2> a(s);
+
+ *(a.Data()+0) = 0;
+ *(a.Data()+5) = 5;
+
+ int i1[] = {0, 0};
+ EXPECT_EQ(0, a(Index(i1)));
+ int i2[] = {1, 2};
+ EXPECT_EQ(5, a(Index(i2)));
+}
+
+TEST(ArrayND, 1DConstructor) {
+ ArrayND<int, 1> a(3);
+
+ EXPECT_EQ(3, a.Shape(0));
+}
+
+TEST(ArrayND, 2DConstructor) {
+ ArrayND<int, 2> a(1, 2);
+
+ EXPECT_EQ(1, a.Shape(0));
+ EXPECT_EQ(2, a.Shape(1));
+}
+
+TEST(ArrayND, 3DConstructor) {
+ ArrayND<int, 3> a(1, 2, 3);
+
+ EXPECT_EQ(1, a.Shape(0));
+ EXPECT_EQ(2, a.Shape(1));
+ EXPECT_EQ(3, a.Shape(2));
+}
+
+TEST(ArrayND, 1DAccessor) {
+ ArrayND<int, 1> a(3);
+ a(0) = 1;
+ a(1) = 2;
+
+ EXPECT_EQ(1, a(0));
+ EXPECT_EQ(2, a(1));
+ EXPECT_EQ(1, *(a.Data()));
+ EXPECT_EQ(2, *(a.Data() + a.Stride(0)));
+}
+
+TEST(ArrayND, 2DAccessor) {
+ ArrayND<int, 2> a(3, 3);
+ a(0, 0) = 1;
+ a(1, 1) = 2;
+
+ EXPECT_EQ(1, a(0, 0));
+ EXPECT_EQ(2, a(1, 1));
+ EXPECT_EQ(1, *(a.Data()));
+ EXPECT_EQ(2, *(a.Data() + a.Stride(0) + a.Stride(1)));
+}
+
+TEST(ArrayND, 3DAccessor) {
+ ArrayND<int, 3> a(3, 3, 3);
+ a(0, 0, 0) = 1;
+ a(1, 1, 1) = 2;
+
+ EXPECT_EQ(1, a(0, 0, 0));
+ EXPECT_EQ(2, a(1, 1, 1));
+ EXPECT_EQ(1, *(a.Data()));
+ EXPECT_EQ(2, *(a.Data() + a.Stride(0) + a.Stride(1) + a.Stride(2)));
+}
+
+TEST(ArrayND, CopyFrom) {
+ ArrayND<int, 3> a(2, 2, 1);
+ a(0, 0, 0) = 1;
+ a(0, 1, 0) = 2;
+ a(1, 0, 0) = 3;
+ a(1, 1, 0) = 4;
+ ArrayND<float, 3> b;
+ b.CopyFrom(a);
+ EXPECT_FLOAT_EQ(1.f, b(0, 0, 0));
+ EXPECT_FLOAT_EQ(2.f, b(0, 1, 0));
+ EXPECT_FLOAT_EQ(3.f, b(1, 0, 0));
+ EXPECT_FLOAT_EQ(4.f, b(1, 1, 0));
+}
+
+TEST(ArrayND, MultiplyElements) {
+ ArrayND<int, 3> a(2, 2, 1);
+ a(0, 0, 0) = 1;
+ a(0, 1, 0) = 2;
+ a(1, 0, 0) = 3;
+ a(1, 1, 0) = 4;
+ ArrayND<int, 3> b(2, 2, 1);
+ b(0, 0, 0) = 6;
+ b(0, 1, 0) = 5;
+ b(1, 0, 0) = 4;
+ b(1, 1, 0) = 3;
+ ArrayND<int, 3> c;
+ MultiplyElements(a, b, &c);
+ EXPECT_FLOAT_EQ(6, c(0, 0, 0));
+ EXPECT_FLOAT_EQ(10, c(0, 1, 0));
+ EXPECT_FLOAT_EQ(12, c(1, 0, 0));
+ EXPECT_FLOAT_EQ(12, c(1, 1, 0));
+}
+
+TEST(ArrayND, IsEqualOperator) {
+ ArrayND<int, 3> a(2, 2, 1);
+ a(0, 0, 0) = 1;
+ a(0, 1, 0) = 2;
+ a(1, 0, 0) = 3;
+ a(1, 1, 0) = 4;
+ ArrayND<int, 3> b(2, 2, 1);
+ b(0, 0, 0) = 1;
+ b(0, 1, 0) = 2;
+ b(1, 0, 0) = 3;
+ b(1, 1, 0) = 4;
+ EXPECT_TRUE(a == b);
+ EXPECT_FALSE(a != b);
+ b(1, 1, 0) = 5;
+ EXPECT_TRUE(a != b);
+ EXPECT_FALSE(a == b);
+}
+
+TEST(Array3D, Sizes) {
+ Array3D<int> array;
+ EXPECT_EQ(array.Height(), 0);
+ EXPECT_EQ(array.Width(), 0);
+ EXPECT_EQ(array.Depth(), 0);
+ EXPECT_EQ(array.Shape(0), 0);
+}
+
+TEST(Array3D, CopyConstructor) {
+ Array3D<int> array(10, 10);
+ array(0, 0) = 1;
+ array(0, 1) = 1;
+ Array3D<int> copy(array);
+ EXPECT_EQ(copy.Height(), 10);
+ EXPECT_EQ(copy.Width(), 10);
+ EXPECT_EQ(copy.Depth(), 1);
+ EXPECT_EQ(copy(0, 0), 1);
+ copy(0, 1) = 2;
+ EXPECT_EQ(array(0, 1), 1);
+}
+
+TEST(Array3D, Assignation) {
+ Array3D<int> array(10, 10);
+ array(0, 0) = 1;
+ array(0, 1) = 1;
+ Array3D<int> copy;
+ copy = array;
+ EXPECT_EQ(copy.Height(), 10);
+ EXPECT_EQ(copy.Width(), 10);
+ EXPECT_EQ(copy.Depth(), 1);
+ EXPECT_EQ(copy(0, 0), 1);
+ copy(0, 1) = 2;
+ EXPECT_EQ(array(0, 1), 1);
+}
+
+TEST(Array3D, Parenthesis) {
+ Array3D<int> array(1, 2, 3);
+ array(0, 1, 0) = 3;
+ EXPECT_EQ(array(0, 1), 3);
+}
+
+TEST(Array3Df, SplitChannels) {
+ Array3Df array(1, 2, 3);
+ array(0, 0, 0) = 1;
+ array(0, 1, 0) = 1;
+ array(0, 0, 1) = 2;
+ array(0, 1, 1) = 2;
+ array(0, 0, 2) = 3;
+ array(0, 1, 2) = 3;
+ Array3Df c0, c1, c2;
+ SplitChannels(array, &c0, &c1, &c2);
+ for (int row = 0; row < 1; ++row) {
+ for (int column = 0; column < 2; ++column) {
+ EXPECT_EQ(array(row, column, 0), c0(row, column));
+ EXPECT_EQ(array(row, column, 1), c1(row, column));
+ EXPECT_EQ(array(row, column, 2), c2(row, column));
+ }
+ }
+}
+
+TEST(ArrayND, MultiplyElementsGeneric) {
+ ArrayND<double, 5> A;
+ ArrayND<int, 5> B;
+ ArrayND<double, 5> C;
+ int shape[] = {1, 3, 5, 7, 1};
+ A.Resize(shape);
+ B.Resize(shape);
+
+ A.Fill(1.1);
+ B.Fill(2);
+ MultiplyElements(A, B, &C);
+
+ ArrayND<double, 5>::Index cIndex;
+ for (int d0 = 0; d0 < shape[0]; ++d0)
+ for (int d1 = 0; d1 < shape[1]; ++d1)
+ for (int d2 = 0; d2 < shape[2]; ++d2)
+ for (int d3 = 0; d3 < shape[3]; ++d3)
+ for (int d4 = 0; d4 < shape[4]; ++d4) {
+ cIndex(0) = d0;
+ cIndex(1) = d1;
+ cIndex(2) = d2;
+ cIndex(3) = d3;
+ cIndex(4) = d4;
+ EXPECT_EQ(2.2, C(cIndex));
+ }
+}
+
+} // namespace
diff --git a/extern/libmv/libmv/image/convolve_test.cc b/extern/libmv/libmv/image/convolve_test.cc
new file mode 100644
index 00000000000..0cdef8e1e72
--- /dev/null
+++ b/extern/libmv/libmv/image/convolve_test.cc
@@ -0,0 +1,110 @@
+// Copyright (c) 2007, 2008 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <iostream>
+
+#include "libmv/image/convolve.h"
+#include "libmv/image/image.h"
+#include "libmv/numeric/numeric.h"
+#include "testing/testing.h"
+
+using namespace libmv;
+
+namespace {
+
+TEST(Convolve, ComputeGaussianKernel) {
+ Vec kernel, derivative;
+ ComputeGaussianKernel(1, &kernel, &derivative);
+ EXPECT_EQ(7, kernel.size());
+ // TODO(keir): Put in a more thorough test here!
+}
+
+TEST(Convolve, ConvolveGaussian) {
+ FloatImage im(10, 10);
+ im.Fill(1);
+ FloatImage blured;
+ ConvolveGaussian(im, 3, &blured);
+ EXPECT_NEAR(im(5, 5), 1, 1e-7);
+}
+
+TEST(Convolve, BoxFilterHorizontal) {
+ FloatImage im(10, 10), convolved, filtered;
+ im.Fill(1);
+ BoxFilterHorizontal(im, 3, &filtered);
+ Vec kernel(3);
+ kernel.setConstant(1.);
+ ConvolveHorizontal(im, kernel, &convolved);
+ EXPECT_EQ(filtered(5, 5), 3);
+ EXPECT_TRUE(filtered == convolved);
+}
+
+TEST(Convolve, BoxFilter) {
+ FloatImage image(5, 5), filtered;
+ // A single 1.0 inside a 5x5 image should expand to a 3x3 square.
+ image.Fill(0);
+ image(2, 2) = 1.0;
+ BoxFilter(image, 3, &filtered);
+ for (int j = 0; j < 5; j++) {
+ for (int i = 0; i < 5; i++) {
+ if (i == 0 || i == 4 || j == 0 || j == 4) {
+ EXPECT_EQ(0.0, filtered(j, i));
+ } else {
+ EXPECT_EQ(1.0, filtered(j, i));
+ }
+ }
+ }
+}
+
+TEST(Convolve, BlurredImageAndDerivativesChannelsFlat) {
+ FloatImage im(10, 10), blurred_and_derivatives;
+ im.Fill(1);
+ BlurredImageAndDerivativesChannels(im, 1.0, &blurred_and_derivatives);
+ EXPECT_NEAR(blurred_and_derivatives(5, 5, 0), 1.0, 1e-7);
+ EXPECT_NEAR(blurred_and_derivatives(5, 5, 1), 0.0, 1e-7);
+ EXPECT_NEAR(blurred_and_derivatives(5, 5, 2), 0.0, 1e-7);
+}
+
+TEST(Convolve, BlurredImageAndDerivativesChannelsHorizontalSlope) {
+ FloatImage image(10, 10), blurred_and_derivatives;
+ for (int j = 0; j < 10; ++j) {
+ for (int i = 0; i < 10; ++i) {
+ image(j, i) = 2*i;
+ }
+ }
+ BlurredImageAndDerivativesChannels(image, 0.9, &blurred_and_derivatives);
+ EXPECT_NEAR(blurred_and_derivatives(5, 5, 0), 10.0, 1e-7);
+ EXPECT_NEAR(blurred_and_derivatives(5, 5, 1), 2.0, 1e-7);
+ EXPECT_NEAR(blurred_and_derivatives(5, 5, 2), 0.0, 1e-7);
+}
+
+TEST(Convolve, BlurredImageAndDerivativesChannelsVerticalSlope) {
+ FloatImage image(10, 10), blurred_and_derivatives;
+ for (int j = 0; j < 10; ++j) {
+ for (int i = 0; i < 10; ++i) {
+ image(j, i) = 2*j;
+ }
+ }
+ BlurredImageAndDerivativesChannels(image, 0.9, &blurred_and_derivatives);
+ EXPECT_NEAR(blurred_and_derivatives(5, 5, 0), 10.0, 1e-7);
+ EXPECT_NEAR(blurred_and_derivatives(5, 5, 1), 0.0, 1e-7);
+ EXPECT_NEAR(blurred_and_derivatives(5, 5, 2), 2.0, 1e-7);
+}
+
+} // namespace
diff --git a/extern/libmv/libmv/image/image_drawing.h b/extern/libmv/libmv/image/image_drawing.h
new file mode 100644
index 00000000000..f50e48b75a3
--- /dev/null
+++ b/extern/libmv/libmv/image/image_drawing.h
@@ -0,0 +1,285 @@
+// Copyright (c) 2009 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+// Generic Image Processing Algorithm (GIPA)
+// Use an ImageModel class that must implement the following :
+//
+// ::Contains(int y, int x) <= Tell if a point is inside or not the image
+// ::operator(int y,int x) <= Modification accessor over the pixel (y,x)
+// ::Width()
+// ::Height()
+
+#ifndef LIBMV_IMAGE_IMAGE_DRAWING_H
+#define LIBMV_IMAGE_IMAGE_DRAWING_H
+
+namespace libmv {
+
+/// Put the pixel in the image to the given color only if the point (xc,yc)
+/// is inside the image.
+template <class Image, class Color>
+inline void safePutPixel(int yc, int xc, const Color & col, Image *pim) {
+ if (!pim)
+ return;
+ if (pim->Contains(yc, xc)) {
+ (*pim)(yc, xc) = col;
+ }
+}
+/// Put the pixel in the image to the given color only if the point (xc,yc)
+/// is inside the image. This function support multi-channel color
+/// \note The color pointer col must have size as the image depth
+template <class Image, class Color>
+inline void safePutPixel(int yc, int xc, const Color *col, Image *pim) {
+ if (!pim)
+ return;
+ if (pim->Contains(yc, xc)) {
+ for (int i = 0; i < pim->Depth(); ++i)
+ (*pim)(yc, xc, i) = *(col + i);
+ }
+}
+
+// Bresenham approach to draw ellipse.
+// http://raphaello.univ-fcomte.fr/ig/algorithme/ExemplesGLUt/BresenhamEllipse.htm
+// Add the rotation of the ellipse.
+// As the algo. use symmetry we must use 4 rotations.
+template <class Image, class Color>
+void DrawEllipse(int xc, int yc, int radiusA, int radiusB,
+ const Color &col, Image *pim, double angle = 0.0) {
+ int a = radiusA;
+ int b = radiusB;
+
+ // Counter Clockwise rotation matrix.
+ double matXY[4] = { cos(angle), sin(angle),
+ -sin(angle), cos(angle)};
+ int x, y;
+ double d1, d2;
+ x = 0;
+ y = b;
+ d1 = b*b - a*a*b + a*a/4;
+
+ float rotX = (matXY[0] * x + matXY[1] * y);
+ float rotY = (matXY[2] * x + matXY[3] * y);
+ safePutPixel(yc + rotY, xc + rotX, col, pim);
+ rotX = (matXY[0] * x - matXY[1] * y);
+ rotY = (matXY[2] * x - matXY[3] * y);
+ safePutPixel(yc + rotY, xc + rotX, col, pim);
+ rotX = (-matXY[0] * x - matXY[1] * y);
+ rotY = (-matXY[2] * x - matXY[3] * y);
+ safePutPixel(yc + rotY, xc + rotX, col, pim);
+ rotX = (-matXY[0] * x + matXY[1] * y);
+ rotY = (-matXY[2] * x + matXY[3] * y);
+ safePutPixel(yc + rotY, xc + rotX, col, pim);
+
+ while (a*a*(y-.5) > b*b*(x+1)) {
+ if (d1 < 0) {
+ d1 += b*b*(2*x+3);
+ ++x;
+ } else {
+ d1 += b*b*(2*x+3) + a*a*(-2*y+2);
+ ++x;
+ --y;
+ }
+ rotX = (matXY[0] * x + matXY[1] * y);
+ rotY = (matXY[2] * x + matXY[3] * y);
+ safePutPixel(yc + rotY, xc + rotX, col, pim);
+ rotX = (matXY[0] * x - matXY[1] * y);
+ rotY = (matXY[2] * x - matXY[3] * y);
+ safePutPixel(yc + rotY, xc + rotX, col, pim);
+ rotX = (-matXY[0] * x - matXY[1] * y);
+ rotY = (-matXY[2] * x - matXY[3] * y);
+ safePutPixel(yc + rotY, xc + rotX, col, pim);
+ rotX = (-matXY[0] * x + matXY[1] * y);
+ rotY = (-matXY[2] * x + matXY[3] * y);
+ safePutPixel(yc + rotY, xc + rotX, col, pim);
+ }
+ d2 = b*b*(x+.5)*(x+.5) + a*a*(y-1)*(y-1) - a*a*b*b;
+ while (y > 0) {
+ if (d2 < 0) {
+ d2 += b*b*(2*x+2) + a*a*(-2*y+3);
+ --y;
+ ++x;
+ } else {
+ d2 += a*a*(-2*y+3);
+ --y;
+ }
+ rotX = (matXY[0] * x + matXY[1] * y);
+ rotY = (matXY[2] * x + matXY[3] * y);
+ safePutPixel(yc + rotY, xc + rotX, col, pim);
+ rotX = (matXY[0] * x - matXY[1] * y);
+ rotY = (matXY[2] * x - matXY[3] * y);
+ safePutPixel(yc + rotY, xc + rotX, col, pim);
+ rotX = (-matXY[0] * x - matXY[1] * y);
+ rotY = (-matXY[2] * x - matXY[3] * y);
+ safePutPixel(yc + rotY, xc + rotX, col, pim);
+ rotX = (-matXY[0] * x + matXY[1] * y);
+ rotY = (-matXY[2] * x + matXY[3] * y);
+ safePutPixel(yc + rotY, xc + rotX, col, pim);
+ }
+}
+
+// Bresenham approach do not allow to draw concentric circle without holes.
+// So it's better the use the Andres method.
+// http://fr.wikipedia.org/wiki/Algorithme_de_tracé_de_cercle_d'Andres.
+template <class Image, class Color>
+void DrawCircle(int x, int y, int radius, const Color &col, Image *pim) {
+ Image &im = *pim;
+ if ( im.Contains(y + radius, x + radius)
+ || im.Contains(y + radius, x - radius)
+ || im.Contains(y - radius, x + radius)
+ || im.Contains(y - radius, x - radius)) {
+ int x1 = 0;
+ int y1 = radius;
+ int d = radius - 1;
+ while (y1 >= x1) {
+ // Draw the point for each octant.
+ safePutPixel( y1 + y, x1 + x, col, pim);
+ safePutPixel( x1 + y, y1 + x, col, pim);
+ safePutPixel( y1 + y, -x1 + x, col, pim);
+ safePutPixel( x1 + y, -y1 + x, col, pim);
+ safePutPixel(-y1 + y, x1 + x, col, pim);
+ safePutPixel(-x1 + y, y1 + x, col, pim);
+ safePutPixel(-y1 + y, -x1 + x, col, pim);
+ safePutPixel(-x1 + y, -y1 + x, col, pim);
+ if (d >= 2 * x1) {
+ d = d - 2 * x1 - 1;
+ x1 += 1;
+ } else {
+ if (d <= 2 * (radius - y1)) {
+ d = d + 2 * y1 - 1;
+ y1 -= 1;
+ } else {
+ d = d + 2 * (y1 - x1 - 1);
+ y1 -= 1;
+ x1 += 1;
+ }
+ }
+ }
+ }
+}
+
+// Bresenham algorithm
+template <class Image, class Color>
+void DrawLine(int xa, int ya, int xb, int yb, const Color &col, Image *pim) {
+ Image &im = *pim;
+
+ // If one point is outside the image
+ // Replace the outside point by the intersection of the line and
+ // the limit (either x=width or y=height).
+ if (!im.Contains(ya, xa) || !im.Contains(yb, xb)) {
+ int width = pim->Width();
+ int height = pim->Height();
+ const bool xdir = xa < xb, ydir = ya < yb;
+ float nx0 = xa, nx1 = xb, ny0 = ya, ny1 = yb,
+ &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
+ &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
+ &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
+ &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
+
+ if (xright < 0 || xleft >= width) return;
+ if (xleft < 0) {
+ yleft -= xleft*(yright - yleft)/(xright - xleft);
+ xleft = 0;
+ }
+ if (xright >= width) {
+ yright -= (xright - width)*(yright - yleft)/(xright - xleft);
+ xright = width - 1;
+ }
+ if (ydown < 0 || yup >= height) return;
+ if (yup < 0) {
+ xup -= yup*(xdown - xup)/(ydown - yup);
+ yup = 0;
+ }
+ if (ydown >= height) {
+ xdown -= (ydown - height)*(xdown - xup)/(ydown - yup);
+ ydown = height - 1;
+ }
+
+ xa = (int) xleft;
+ xb = (int) xright;
+ ya = (int) yleft;
+ yb = (int) yright;
+ }
+
+ int xbas, xhaut, ybas, yhaut;
+ // Check the condition ybas < yhaut.
+ if (ya <= yb) {
+ xbas = xa;
+ ybas = ya;
+ xhaut = xb;
+ yhaut = yb;
+ } else {
+ xbas = xb;
+ ybas = yb;
+ xhaut = xa;
+ yhaut = ya;
+ }
+ // Initialize slope.
+ int x, y, dx, dy, incrmX, incrmY, dp, N, S;
+ dx = xhaut - xbas;
+ dy = yhaut - ybas;
+ if (dx > 0) { // If xhaut > xbas we will increment X.
+ incrmX = 1;
+ } else {
+ incrmX = -1; // else we will decrement X.
+ dx *= -1;
+ }
+ if (dy > 0) { // Positive slope will increment X.
+ incrmY = 1;
+ } else { // Negative slope.
+ incrmY = -1;
+ }
+ if (dx >= dy) {
+ dp = 2 * dy - dx;
+ S = 2 * dy;
+ N = 2 * (dy - dx);
+ y = ybas;
+ x = xbas;
+ while (x != xhaut) {
+ safePutPixel(y, x, col, pim);
+ x += incrmX;
+ if (dp <= 0) { // Go in direction of the South Pixel.
+ dp += S;
+ } else { // Go to the North.
+ dp += N;
+ y+=incrmY;
+ }
+ }
+ } else {
+ dp = 2 * dx - dy;
+ S = 2 * dx;
+ N = 2 * (dx - dy);
+ x = xbas;
+ y = ybas;
+ while (y < yhaut) {
+ safePutPixel(y, x, col, pim);
+ y += incrmY;
+ if (dp <= 0) { // Go in direction of the South Pixel.
+ dp += S;
+ } else { // Go to the North.
+ dp += N;
+ x += incrmX;
+ }
+ }
+ }
+ safePutPixel(y, x, col, pim);
+}
+
+} // namespace libmv
+
+#endif // LIBMV_IMAGE_IMAGE_DRAWING_H
diff --git a/extern/libmv/libmv/image/image_test.cc b/extern/libmv/libmv/image/image_test.cc
new file mode 100644
index 00000000000..241f49f2244
--- /dev/null
+++ b/extern/libmv/libmv/image/image_test.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2007, 2008 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <iostream>
+
+#include "libmv/image/image.h"
+#include "testing/testing.h"
+
+using libmv::Image;
+using libmv::Array3Df;
+
+namespace {
+
+TEST(Image, SimpleImageAccessors) {
+ Array3Df *array = new Array3Df(2, 3);
+ Image image(array);
+ EXPECT_EQ(array, image.AsArray3Df());
+ EXPECT_TRUE(NULL == image.AsArray3Du());
+}
+
+TEST(Image, MemorySizeInBytes) {
+ Array3Df *array = new Array3Df(2, 3);
+ Image image(array);
+ int size = sizeof(image) + array->MemorySizeInBytes();
+ EXPECT_EQ(size, image.MemorySizeInBytes());
+}
+
+} // namespace
diff --git a/extern/libmv/libmv/image/sample_test.cc b/extern/libmv/libmv/image/sample_test.cc
new file mode 100644
index 00000000000..c8a0ce470c2
--- /dev/null
+++ b/extern/libmv/libmv/image/sample_test.cc
@@ -0,0 +1,89 @@
+// Copyright (c) 2007, 2008 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/image/sample.h"
+#include "testing/testing.h"
+
+using namespace libmv;
+
+namespace {
+
+TEST(Image, Nearest) {
+ Array3Du image(2, 2);
+ image(0, 0) = 0;
+ image(0, 1) = 1;
+ image(1, 0) = 2;
+ image(1, 1) = 3;
+ EXPECT_EQ(0, SampleNearest(image, -0.4f, -0.4f));
+ EXPECT_EQ(0, SampleNearest(image, 0.4f, 0.4f));
+ EXPECT_EQ(3, SampleNearest(image, 0.6f, 0.6f));
+ EXPECT_EQ(3, SampleNearest(image, 1.4f, 1.4f));
+}
+
+TEST(Image, Linear) {
+ Array3Df image(2, 2);
+ image(0, 0) = 0;
+ image(0, 1) = 1;
+ image(1, 0) = 2;
+ image(1, 1) = 3;
+ EXPECT_EQ(1.5, SampleLinear(image, 0.5, 0.5));
+}
+
+TEST(Image, DownsampleBy2) {
+ Array3Df image(2, 2);
+ image(0, 0) = 0;
+ image(0, 1) = 1;
+ image(1, 0) = 2;
+ image(1, 1) = 3;
+ Array3Df resampled_image;
+ DownsampleChannelsBy2(image, &resampled_image);
+ ASSERT_EQ(1, resampled_image.Height());
+ ASSERT_EQ(1, resampled_image.Width());
+ ASSERT_EQ(1, resampled_image.Depth());
+ EXPECT_FLOAT_EQ(6./4., resampled_image(0, 0));
+}
+
+TEST(Image, DownsampleBy2MultiChannel) {
+ Array3Df image(2, 2, 3);
+ image(0, 0, 0) = 0;
+ image(0, 1, 0) = 1;
+ image(1, 0, 0) = 2;
+ image(1, 1, 0) = 3;
+
+ image(0, 0, 1) = 5;
+ image(0, 1, 1) = 6;
+ image(1, 0, 1) = 7;
+ image(1, 1, 1) = 8;
+
+ image(0, 0, 2) = 9;
+ image(0, 1, 2) = 10;
+ image(1, 0, 2) = 11;
+ image(1, 1, 2) = 12;
+
+ Array3Df resampled_image;
+ DownsampleChannelsBy2(image, &resampled_image);
+ ASSERT_EQ(1, resampled_image.Height());
+ ASSERT_EQ(1, resampled_image.Width());
+ ASSERT_EQ(3, resampled_image.Depth());
+ EXPECT_FLOAT_EQ((0+1+2+3)/4., resampled_image(0, 0, 0));
+ EXPECT_FLOAT_EQ((5+6+7+8)/4., resampled_image(0, 0, 1));
+ EXPECT_FLOAT_EQ((9+10+11+12)/4., resampled_image(0, 0, 2));
+}
+} // namespace
diff --git a/extern/libmv/libmv/image/tuple_test.cc b/extern/libmv/libmv/image/tuple_test.cc
new file mode 100644
index 00000000000..df44e5638b5
--- /dev/null
+++ b/extern/libmv/libmv/image/tuple_test.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2007, 2008 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/image/tuple.h"
+#include "testing/testing.h"
+
+#include <algorithm>
+
+using libmv::Tuple;
+
+namespace {
+
+TEST(Tuple, InitConstantValue) {
+ Tuple<int, 3> t(5);
+ EXPECT_EQ(t(0), 5);
+ EXPECT_EQ(t(0), 5);
+ EXPECT_EQ(t(0), 5);
+}
+
+TEST(Tuple, InitFromPointer) {
+ float vals[3] = {1.0f, 2.0f, 3.0f};
+
+ Tuple<float, 3> t(vals);
+ for (int i = 0; i < 3; i++)
+ EXPECT_EQ(t(i), vals[i]);
+
+ Tuple<int, 3> b(t);
+ EXPECT_EQ(b(0), int(vals[0]));
+ EXPECT_EQ(b(1), int(vals[1]));
+ EXPECT_EQ(b(2), int(vals[2]));
+}
+
+TEST(Tuple, Swap) {
+ unsigned char vala[3] = {1, 2, 3};
+ unsigned char valb[3] = {4, 5, 6};
+
+ Tuple<unsigned char, 3> a(vala);
+ Tuple<unsigned char, 3> b(valb);
+
+ std::swap(a, b);
+
+ EXPECT_EQ(a(0), int(valb[0]));
+ EXPECT_EQ(a(1), int(valb[1]));
+ EXPECT_EQ(a(2), int(valb[2]));
+ EXPECT_EQ(b(0), int(vala[0]));
+ EXPECT_EQ(b(1), int(vala[1]));
+ EXPECT_EQ(b(2), int(vala[2]));
+}
+
+TEST(Tuple, IsEqualOperator) {
+ Tuple<int, 3> a;
+ a(0) = 1;
+ a(1) = 2;
+ a(2) = 3;
+ Tuple<int, 3> b;
+ b(0) = 1;
+ b(1) = 2;
+ b(2) = 3;
+ EXPECT_TRUE(a == b);
+ EXPECT_FALSE(a != b);
+ b(1) = 5;
+ EXPECT_TRUE(a != b);
+ EXPECT_FALSE(a == b);
+}
+
+} // namespace
diff --git a/extern/libmv/libmv/multiview/euclidean_resection_test.cc b/extern/libmv/libmv/multiview/euclidean_resection_test.cc
new file mode 100644
index 00000000000..5ec9dbda3cf
--- /dev/null
+++ b/extern/libmv/libmv/multiview/euclidean_resection_test.cc
@@ -0,0 +1,237 @@
+// Copyright (c) 2009 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/multiview/euclidean_resection.h"
+#include "libmv/numeric/numeric.h"
+#include "libmv/logging/logging.h"
+#include "libmv/multiview/projection.h"
+#include "testing/testing.h"
+
+using namespace libmv::euclidean_resection;
+using namespace libmv;
+
+// Generates all necessary inputs and expected outputs for EuclideanResection.
+void CreateCameraSystem(const Mat3& KK,
+ const Mat3X& x_image,
+ const Vec& X_distances,
+ const Mat3& R_input,
+ const Vec3& T_input,
+ Mat2X *x_camera,
+ Mat3X *X_world,
+ Mat3 *R_expected,
+ Vec3 *T_expected) {
+ int num_points = x_image.cols();
+
+ Mat3X x_unit_cam(3, num_points);
+ x_unit_cam = KK.inverse() * x_image;
+
+ // Create normalized camera coordinates to be used as an input to the PnP
+ // function, instead of using NormalizeColumnVectors(&x_unit_cam).
+ *x_camera = x_unit_cam.block(0, 0, 2, num_points);
+ for (int i = 0; i < num_points; ++i) {
+ x_unit_cam.col(i).normalize();
+ }
+
+ // Create the 3D points in the camera system.
+ Mat X_camera(3, num_points);
+ for (int i = 0; i < num_points; ++i) {
+ X_camera.col(i) = X_distances(i) * x_unit_cam.col(i);
+ }
+
+ // Apply the transformation to the camera 3D points
+ Mat translation_matrix(3, num_points);
+ translation_matrix.row(0).setConstant(T_input(0));
+ translation_matrix.row(1).setConstant(T_input(1));
+ translation_matrix.row(2).setConstant(T_input(2));
+
+ *X_world = R_input * X_camera + translation_matrix;
+
+ // Create the expected result for comparison.
+ *R_expected = R_input.transpose();
+ *T_expected = *R_expected * (-T_input);
+};
+
+TEST(AbsoluteOrientation, QuaternionSolution) {
+ int num_points = 4;
+ Mat X;
+ Mat Xp;
+ X = 100 * Mat::Random(3, num_points);
+
+ // Create a random translation and rotation.
+ Mat3 R_input;
+ R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ())
+ * Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY())
+ * Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
+
+ Vec3 t_input;
+ t_input.setRandom();
+ t_input = 100 * t_input;
+
+ Mat translation_matrix(3, num_points);
+ translation_matrix.row(0).setConstant(t_input(0));
+ translation_matrix.row(1).setConstant(t_input(1));
+ translation_matrix.row(2).setConstant(t_input(2));
+
+ // Create the transformed 3D points Xp as Xp = R * X + t.
+ Xp = R_input * X + translation_matrix;
+
+ // Output variables.
+ Mat3 R;
+ Vec3 t;
+
+ AbsoluteOrientation(X, Xp, &R, &t);
+
+ EXPECT_MATRIX_NEAR(t, t_input, 1e-6);
+ EXPECT_MATRIX_NEAR(R, R_input, 1e-8);
+}
+
+TEST(EuclideanResection, Points4KnownImagePointsRandomTranslationRotation) {
+ // In this test only the translation and rotation are random. The image
+ // points are selected from a real case and are well conditioned.
+ Vec2i image_dimensions;
+ image_dimensions << 1600, 1200;
+
+ Mat3 KK;
+ KK << 2796, 0, 804,
+ 0 , 2796, 641,
+ 0, 0, 1;
+
+ // The real image points.
+ int num_points = 4;
+ Mat3X x_image(3, num_points);
+ x_image << 1164.06, 734.948, 749.599, 430.727,
+ 681.386, 844.59, 496.315, 580.775,
+ 1, 1, 1, 1;
+
+
+ // A vector of the 4 distances to the 3D points.
+ Vec X_distances = 100 * Vec::Random(num_points).array().abs();
+
+ // Create the random camera motion R and t that resection should recover.
+ Mat3 R_input;
+ R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ())
+ * Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY())
+ * Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
+
+ Vec3 T_input;
+ T_input.setRandom();
+ T_input = 100 * T_input;
+
+ // Create the camera system, also getting the expected result of the
+ // transformation.
+ Mat3 R_expected;
+ Vec3 T_expected;
+ Mat3X X_world;
+ Mat2X x_camera;
+ CreateCameraSystem(KK, x_image, X_distances, R_input, T_input,
+ &x_camera, &X_world, &R_expected, &T_expected);
+
+ // Finally, run the code under test.
+ Mat3 R_output;
+ Vec3 T_output;
+ EuclideanResection(x_camera, X_world,
+ &R_output, &T_output,
+ RESECTION_ANSAR_DANIILIDIS);
+
+ EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
+ EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
+
+ // For now, the EPnP doesn't have a non-linear optimization step and so is
+ // not precise enough with only 4 points.
+ //
+ // TODO(jmichot): Reenable this test when there is nonlinear refinement.
+#if 0
+ R_output.setIdentity();
+ T_output.setZero();
+
+ EuclideanResection(x_camera, X_world,
+ &R_output, &T_output,
+ RESECTION_EPNP);
+
+ EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
+ EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);*/
+#endif
+}
+
+// TODO(jmichot): Reduce the code duplication here with the code above.
+TEST(EuclideanResection, Points6AllRandomInput) {
+ Mat3 KK;
+ KK << 2796, 0, 804,
+ 0 , 2796, 641,
+ 0, 0, 1;
+
+ // Create random image points for a 1600x1200 image.
+ int w = 1600;
+ int h = 1200;
+ int num_points = 6;
+ Mat3X x_image(3, num_points);
+ x_image.row(0) = w * Vec::Random(num_points).array().abs();
+ x_image.row(1) = h * Vec::Random(num_points).array().abs();
+ x_image.row(2).setOnes();
+
+ // Normalized camera coordinates to be used as an input to the PnP function.
+ Mat2X x_camera;
+ Vec X_distances = 100 * Vec::Random(num_points).array().abs();
+
+ // Create the random camera motion R and t that resection should recover.
+ Mat3 R_input;
+ R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ())
+ * Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY())
+ * Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
+
+ Vec3 T_input;
+ T_input.setRandom();
+ T_input = 100 * T_input;
+
+ // Create the camera system.
+ Mat3 R_expected;
+ Vec3 T_expected;
+ Mat3X X_world;
+ CreateCameraSystem(KK, x_image, X_distances, R_input, T_input,
+ &x_camera, &X_world, &R_expected, &T_expected);
+
+ // Test each of the resection methods.
+ {
+ Mat3 R_output;
+ Vec3 T_output;
+ EuclideanResection(x_camera, X_world,
+ &R_output, &T_output,
+ RESECTION_ANSAR_DANIILIDIS);
+ EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
+ EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
+ }
+ {
+ Mat3 R_output;
+ Vec3 T_output;
+ EuclideanResection(x_camera, X_world,
+ &R_output, &T_output,
+ RESECTION_EPNP);
+ EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
+ EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
+ }
+ {
+ Mat3 R_output;
+ Vec3 T_output;
+ EuclideanResection(x_image, X_world, KK,
+ &R_output, &T_output);
+ EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
+ EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
+ }
+}
diff --git a/extern/libmv/libmv/multiview/fundamental_test.cc b/extern/libmv/libmv/multiview/fundamental_test.cc
new file mode 100644
index 00000000000..da0eb449b8f
--- /dev/null
+++ b/extern/libmv/libmv/multiview/fundamental_test.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2007, 2008 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <iostream>
+
+#include "libmv/logging/logging.h"
+#include "libmv/multiview/conditioning.h"
+#include "libmv/multiview/fundamental.h"
+#include "libmv/multiview/projection.h"
+#include "libmv/multiview/test_data_sets.h"
+#include "libmv/numeric/numeric.h"
+#include "testing/testing.h"
+
+namespace {
+
+using namespace libmv;
+
+TEST(Fundamental, FundamentalFromProjections) {
+ Mat34 P1_gt, P2_gt;
+ P1_gt << 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0;
+ P2_gt << 1, 1, 1, 3,
+ 0, 2, 0, 3,
+ 0, 1, 1, 0;
+ Mat3 F_gt;
+ FundamentalFromProjections(P1_gt, P2_gt, &F_gt);
+
+ Mat34 P1, P2;
+ ProjectionsFromFundamental(F_gt, &P1, &P2);
+
+ Mat3 F;
+ FundamentalFromProjections(P1, P2, &F);
+
+ EXPECT_MATRIX_PROP(F_gt, F, 1e-6);
+}
+
+TEST(Fundamental, PreconditionerFromPoints) {
+ int n = 4;
+ Mat points(2, n);
+ points << 0, 0, 1, 1,
+ 0, 2, 1, 3;
+
+ Mat3 T;
+ PreconditionerFromPoints(points, &T);
+
+ Mat normalized_points;
+ ApplyTransformationToPoints(points, T, &normalized_points);
+
+ Vec mean, variance;
+ MeanAndVarianceAlongRows(normalized_points, &mean, &variance);
+
+ EXPECT_NEAR(0, mean(0), 1e-8);
+ EXPECT_NEAR(0, mean(1), 1e-8);
+ EXPECT_NEAR(2, variance(0), 1e-8);
+ EXPECT_NEAR(2, variance(1), 1e-8);
+}
+
+TEST(Fundamental, EssentialFromFundamental) {
+ TwoViewDataSet d = TwoRealisticCameras();
+
+ Mat3 E_from_Rt;
+ EssentialFromRt(d.R1, d.t1, d.R2, d.t2, &E_from_Rt);
+
+ Mat3 E_from_F;
+ EssentialFromFundamental(d.F, d.K1, d.K2, &E_from_F);
+
+ EXPECT_MATRIX_PROP(E_from_Rt, E_from_F, 1e-6);
+}
+
+TEST(Fundamental, MotionFromEssential) {
+ TwoViewDataSet d = TwoRealisticCameras();
+
+ Mat3 E;
+ EssentialFromRt(d.R1, d.t1, d.R2, d.t2, &E);
+
+ Mat3 R;
+ Vec3 t;
+ RelativeCameraMotion(d.R1, d.t1, d.R2, d.t2, &R, &t);
+ NormalizeL2(&t);
+
+ std::vector<Mat3> Rs;
+ std::vector<Vec3> ts;
+ MotionFromEssential(E, &Rs, &ts);
+ bool one_solution_is_correct = false;
+ for (size_t i = 0; i < Rs.size(); ++i) {
+ if (FrobeniusDistance(Rs[i], R) < 1e-8 && DistanceL2(ts[i], t) < 1e-8) {
+ one_solution_is_correct = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(one_solution_is_correct);
+}
+
+TEST(Fundamental, MotionFromEssentialChooseSolution) {
+ TwoViewDataSet d = TwoRealisticCameras();
+
+ Mat3 E;
+ EssentialFromRt(d.R1, d.t1, d.R2, d.t2, &E);
+
+ Mat3 R;
+ Vec3 t;
+ RelativeCameraMotion(d.R1, d.t1, d.R2, d.t2, &R, &t);
+ NormalizeL2(&t);
+
+ std::vector<Mat3> Rs;
+ std::vector<Vec3> ts;
+ MotionFromEssential(E, &Rs, &ts);
+
+ Vec2 x1, x2;
+ MatrixColumn(d.x1, 0, &x1);
+ MatrixColumn(d.x2, 0, &x2);
+ int solution = MotionFromEssentialChooseSolution(Rs, ts, d.K1, x1, d.K2, x2);
+
+ EXPECT_LE(0, solution);
+ EXPECT_LE(solution, 3);
+ EXPECT_LE(FrobeniusDistance(Rs[solution], R), 1e-8);
+ EXPECT_LE(DistanceL2(ts[solution], t), 1e-8);
+}
+
+TEST(Fundamental, MotionFromEssentialAndCorrespondence) {
+ TwoViewDataSet d = TwoRealisticCameras();
+
+ Mat3 E;
+ EssentialFromRt(d.R1, d.t1, d.R2, d.t2, &E);
+
+ Mat3 R;
+ Vec3 t;
+ RelativeCameraMotion(d.R1, d.t1, d.R2, d.t2, &R, &t);
+ NormalizeL2(&t);
+
+ Vec2 x1, x2;
+ MatrixColumn(d.x1, 0, &x1);
+ MatrixColumn(d.x2, 0, &x2);
+
+ Mat3 R_estimated;
+ Vec3 t_estimated;
+ MotionFromEssentialAndCorrespondence(E, d.K1, x1, d.K2, x2,
+ &R_estimated, &t_estimated);
+
+ EXPECT_LE(FrobeniusDistance(R_estimated, R), 1e-8);
+ EXPECT_LE(DistanceL2(t_estimated, t), 1e-8);
+}
+
+} // namespace
diff --git a/extern/libmv/libmv/multiview/homography_error.h b/extern/libmv/libmv/multiview/homography_error.h
new file mode 100644
index 00000000000..f8b9d45e73c
--- /dev/null
+++ b/extern/libmv/libmv/multiview/homography_error.h
@@ -0,0 +1,248 @@
+// Copyright (c) 2011 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef LIBMV_MULTIVIEW_HOMOGRAPHY_ERRORS_H_
+#define LIBMV_MULTIVIEW_HOMOGRAPHY_ERRORS_H_
+
+#include "libmv/multiview/projection.h"
+
+namespace libmv {
+namespace homography {
+namespace homography2D {
+
+ /**
+ * Structure for estimating the asymmetric error between a vector x2 and the
+ * transformed x1 such that
+ * Error = ||x2 - Psi(H * x1)||^2
+ * where Psi is the function that transforms homogeneous to euclidean coords.
+ * \note It should be distributed as Chi-squared with k = 2.
+ */
+struct AsymmetricError {
+ /**
+ * Computes the asymmetric residuals between a set of 2D points x2 and the
+ * transformed 2D point set x1 such that
+ * Residuals_i = x2_i - Psi(H * x1_i)
+ * where Psi is the function that transforms homogeneous to euclidean coords.
+ *
+ * \param[in] H The 3x3 homography matrix.
+ * The estimated homography should approximatelly hold the condition y = H x.
+ * \param[in] x1 A set of 2D points (2xN or 3xN matrix of column vectors).
+ * \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors).
+ * \param[out] dx A 2xN matrix of column vectors of residuals errors
+ */
+ static void Residuals(const Mat &H, const Mat &x1,
+ const Mat &x2, Mat2X *dx) {
+ dx->resize(2, x1.cols());
+ Mat3X x2h_est;
+ if (x1.rows() == 2)
+ x2h_est = H * EuclideanToHomogeneous(static_cast<Mat2X>(x1));
+ else
+ x2h_est = H * x1;
+ dx->row(0) = x2h_est.row(0).array() / x2h_est.row(2).array();
+ dx->row(1) = x2h_est.row(1).array() / x2h_est.row(2).array();
+ if (x2.rows() == 2)
+ *dx = x2 - *dx;
+ else
+ *dx = HomogeneousToEuclidean(static_cast<Mat3X>(x2)) - *dx;
+ }
+ /**
+ * Computes the asymmetric residuals between a 2D point x2 and the transformed
+ * 2D point x1 such that
+ * Residuals = x2 - Psi(H * x1)
+ * where Psi is the function that transforms homogeneous to euclidean coords.
+ *
+ * \param[in] H The 3x3 homography matrix.
+ * The estimated homography should approximatelly hold the condition y = H x.
+ * \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
+ * \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
+ * \param[out] dx A vector of size 2 of the residual error
+ */
+ static void Residuals(const Mat &H, const Vec &x1,
+ const Vec &x2, Vec2 *dx) {
+ Vec3 x2h_est;
+ if (x1.rows() == 2)
+ x2h_est = H * EuclideanToHomogeneous(static_cast<Vec2>(x1));
+ else
+ x2h_est = H * x1;
+ if (x2.rows() == 2)
+ *dx = x2 - x2h_est.head<2>() / x2h_est[2];
+ else
+ *dx = HomogeneousToEuclidean(static_cast<Vec3>(x2)) -
+ x2h_est.head<2>() / x2h_est[2];
+ }
+ /**
+ * Computes the squared norm of the residuals between a set of 2D points x2
+ * and the transformed 2D point set x1 such that
+ * Error = || x2 - Psi(H * x1) ||^2
+ * where Psi is the function that transforms homogeneous to euclidean coords.
+ *
+ * \param[in] H The 3x3 homography matrix.
+ * The estimated homography should approximatelly hold the condition y = H x.
+ * \param[in] x1 A set of 2D points (2xN or 3xN matrix of column vectors).
+ * \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors).
+ * \return The squared norm of the asymmetric residuals errors
+ */
+ static double Error(const Mat &H, const Mat &x1, const Mat &x2) {
+ Mat2X dx;
+ Residuals(H, x1, x2, &dx);
+ return dx.squaredNorm();
+ }
+ /**
+ * Computes the squared norm of the residuals between a 2D point x2 and the
+ * transformed 2D point x1 such that rms = || x2 - Psi(H * x1) ||^2
+ * where Psi is the function that transforms homogeneous to euclidean coords.
+ *
+ * \param[in] H The 3x3 homography matrix.
+ * The estimated homography should approximatelly hold the condition y = H x.
+ * \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
+ * \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
+ * \return The squared norm of the asymmetric residual error
+ */
+ static double Error(const Mat &H, const Vec &x1, const Vec &x2) {
+ Vec2 dx;
+ Residuals(H, x1, x2, &dx);
+ return dx.squaredNorm();
+ }
+};
+
+ /**
+ * Structure for estimating the symmetric error
+ * between a vector x2 and the transformed x1 such that
+ * Error = ||x2 - Psi(H * x1)||^2 + ||x1 - Psi(H^-1 * x2)||^2
+ * where Psi is the function that transforms homogeneous to euclidean coords.
+ * \note It should be distributed as Chi-squared with k = 4.
+ */
+struct SymmetricError {
+ /**
+ * Computes the squared norm of the residuals between x2 and the
+ * transformed x1 such that
+ * Error = ||x2 - Psi(H * x1)||^2 + ||x1 - Psi(H^-1 * x2)||^2
+ * where Psi is the function that transforms homogeneous to euclidean coords.
+ *
+ * \param[in] H The 3x3 homography matrix.
+ * The estimated homography should approximatelly hold the condition y = H x.
+ * \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
+ * \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
+ * \return The squared norm of the symmetric residuals errors
+ */
+ static double Error(const Mat &H, const Vec &x1, const Vec &x2) {
+ // TODO(keir): This is awesomely inefficient because it does a 3x3
+ // inversion for each evaluation.
+ Mat3 Hinv = H.inverse();
+ return AsymmetricError::Error(H, x1, x2) +
+ AsymmetricError::Error(Hinv, x2, x1);
+ }
+ // TODO(julien) Add residuals function \see AsymmetricError
+};
+ /**
+ * Structure for estimating the algebraic error (cross product)
+ * between a vector x2 and the transformed x1 such that
+ * Error = ||[x2] * H * x1||^^2
+ * where [x2] is the skew matrix of x2.
+ */
+struct AlgebraicError {
+ // TODO(julien) Make an AlgebraicError2Rows and AlgebraicError3Rows
+
+ /**
+ * Computes the algebraic residuals (cross product) between a set of 2D
+ * points x2 and the transformed 2D point set x1 such that
+ * [x2] * H * x1 where [x2] is the skew matrix of x2.
+ *
+ * \param[in] H The 3x3 homography matrix.
+ * The estimated homography should approximatelly hold the condition y = H x.
+ * \param[in] x1 A set of 2D points (2xN or 3xN matrix of column vectors).
+ * \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors).
+ * \param[out] dx A 3xN matrix of column vectors of residuals errors
+ */
+ static void Residuals(const Mat &H, const Mat &x1,
+ const Mat &x2, Mat3X *dx) {
+ dx->resize(3, x1.cols());
+ Vec3 col;
+ for (int i = 0; i < x1.cols(); ++i) {
+ Residuals(H, x1.col(i), x2.col(i), &col);
+ dx->col(i) = col;
+ }
+ }
+ /**
+ * Computes the algebraic residuals (cross product) between a 2D point x2
+ * and the transformed 2D point x1 such that
+ * [x2] * H * x1 where [x2] is the skew matrix of x2.
+ *
+ * \param[in] H The 3x3 homography matrix.
+ * The estimated homography should approximatelly hold the condition y = H x.
+ * \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
+ * \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
+ * \param[out] dx A vector of size 3 of the residual error
+ */
+ static void Residuals(const Mat &H, const Vec &x1,
+ const Vec &x2, Vec3 *dx) {
+ Vec3 x2h_est;
+ if (x1.rows() == 2)
+ x2h_est = H * EuclideanToHomogeneous(static_cast<Vec2>(x1));
+ else
+ x2h_est = H * x1;
+ if (x2.rows() == 2)
+ *dx = SkewMat(EuclideanToHomogeneous(static_cast<Vec2>(x2))) * x2h_est;
+ else
+ *dx = SkewMat(x2) * x2h_est;
+ // TODO(julien) This is inefficient since it creates an
+ // identical 3x3 skew matrix for each evaluation.
+ }
+ /**
+ * Computes the squared norm of the algebraic residuals between a set of 2D
+ * points x2 and the transformed 2D point set x1 such that
+ * [x2] * H * x1 where [x2] is the skew matrix of x2.
+ *
+ * \param[in] H The 3x3 homography matrix.
+ * The estimated homography should approximatelly hold the condition y = H x.
+ * \param[in] x1 A set of 2D points (2xN or 3xN matrix of column vectors).
+ * \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors).
+ * \return The squared norm of the asymmetric residuals errors
+ */
+ static double Error(const Mat &H, const Mat &x1, const Mat &x2) {
+ Mat3X dx;
+ Residuals(H, x1, x2, &dx);
+ return dx.squaredNorm();
+ }
+ /**
+ * Computes the squared norm of the algebraic residuals between a 2D point x2
+ * and the transformed 2D point x1 such that
+ * [x2] * H * x1 where [x2] is the skew matrix of x2.
+ *
+ * \param[in] H The 3x3 homography matrix.
+ * The estimated homography should approximatelly hold the condition y = H x.
+ * \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
+ * \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
+ * \return The squared norm of the asymmetric residual error
+ */
+ static double Error(const Mat &H, const Vec &x1, const Vec &x2) {
+ Vec3 dx;
+ Residuals(H, x1, x2, &dx);
+ return dx.squaredNorm();
+ }
+};
+// TODO(keir): Add error based on ideal points.
+
+} // namespace homography2D
+// TODO(julien) add homography3D errors
+} // namespace homography
+} // namespace libmv
+
+#endif // LIBMV_MULTIVIEW_HOMOGRAPHY_ERRORS_H_
diff --git a/extern/libmv/libmv/multiview/homography_test.cc b/extern/libmv/libmv/multiview/homography_test.cc
new file mode 100644
index 00000000000..8d7266e3d11
--- /dev/null
+++ b/extern/libmv/libmv/multiview/homography_test.cc
@@ -0,0 +1,261 @@
+// Copyright (c) 2011 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "testing/testing.h"
+#include "libmv/logging/logging.h"
+#include "libmv/multiview/projection.h"
+#include "libmv/multiview/homography.h"
+
+namespace {
+using namespace libmv;
+
+namespace {
+
+// Check whether homography transform M actually transforms
+// given vectors x1 to x2. Used to check validness of a reconstructed
+// homography matrix.
+// TODO(sergey): Consider using this in all tests since possible homography
+// matrix is not fixed to a single value and different-looking matrices
+// might actually crrespond to the same exact transform.
+void CheckHomography2DTransform(const Mat3 &H,
+ const Mat &x1,
+ const Mat &x2) {
+ for (int i = 0; i < x2.cols(); ++i) {
+ Vec3 x2_expected = x2.col(i);
+ Vec3 x2_observed = H * x1.col(i);
+ x2_observed /= x2_observed(2);
+ EXPECT_MATRIX_NEAR(x2_expected, x2_observed, 1e-8);
+ }
+}
+
+} // namespace
+
+TEST(Homography2DTest, Rotation45AndTranslationXY) {
+ Mat x1(3, 4);
+ x1 << 0, 1, 0, 5,
+ 0, 0, 2, 3,
+ 1, 1, 1, 1;
+
+ double angle = 45.0;
+ Mat3 m;
+ m << cos(angle), -sin(angle), -2,
+ sin(angle), cos(angle), 5,
+ 0, 0, 1;
+
+ Mat x2 = x1;
+ // Transform point from ground truth matrix
+ for (int i = 0; i < x2.cols(); ++i)
+ x2.col(i) = m * x1.col(i);
+
+ Mat3 homography_mat;
+ EXPECT_TRUE(Homography2DFromCorrespondencesLinear(x1, x2, &homography_mat));
+ VLOG(1) << "Mat Homography2D ";
+ VLOG(1) << homography_mat;
+ VLOG(1) << "Mat GT ";
+ VLOG(1) << m;
+ EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
+}
+
+TEST(Homography2DTest, AffineGeneral4) {
+ // TODO(julien) find why it doesn't work with 4 points!!!
+ Mat x1(3, 4);
+ x1 << 0, 1, 0, 2,
+ 0, 0, 1, 2,
+ 1, 1, 1, 1;
+ Mat3 m;
+ m << 3, -1, 4,
+ 6, -2, -3,
+ 0, 0, 1;
+
+ Mat x2 = x1;
+ for (int i = 0; i < x2.cols(); ++i) {
+ x2.col(i) = m * x1.col(i);
+ }
+
+ Mat3 homography_mat;
+ EXPECT_TRUE(Homography2DFromCorrespondencesLinear(x1, x2, &homography_mat));
+ VLOG(1) << "Mat Homography2D";
+ VLOG(1) << homography_mat;
+ CheckHomography2DTransform(homography_mat, x1, x2);
+
+ // Test with euclidean coordinates
+ Mat eX1, eX2;
+ HomogeneousToEuclidean(x1, &eX1);
+ HomogeneousToEuclidean(x2, &eX2);
+ homography_mat.setIdentity();
+ EXPECT_TRUE(Homography2DFromCorrespondencesLinear(eX1, eX2, &homography_mat));
+
+ VLOG(1) << "Mat Homography2D ";
+ VLOG(1) << homography_mat;
+ CheckHomography2DTransform(homography_mat, x1, x2);
+}
+
+TEST(Homography2DTest, AffineGeneral5) {
+ Mat x1(3, 5);
+ x1 << 0, 1, 0, 2, 5,
+ 0, 0, 1, 2, 2,
+ 1, 1, 1, 1, 1;
+ Mat3 m;
+ m << 3, -1, 4,
+ 6, -2, -3,
+ 0, 0, 1;
+
+ Mat x2 = x1;
+ for (int i = 0; i < x2.cols(); ++i)
+ x2.col(i) = m * x1.col(i);
+
+ Mat3 homography_mat;
+ EXPECT_TRUE(Homography2DFromCorrespondencesLinear(x1, x2, &homography_mat));
+
+ VLOG(1) << "Mat Homography2D ";
+ VLOG(1) << homography_mat;
+ EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
+
+ // Test with euclidean coordinates
+ Mat eX1, eX2;
+ HomogeneousToEuclidean(x1, &eX1);
+ HomogeneousToEuclidean(x2, &eX2);
+ homography_mat.setIdentity();
+ EXPECT_TRUE(Homography2DFromCorrespondencesLinear(eX1, eX2, &homography_mat));
+
+ VLOG(1) << "Mat Homography2D ";
+ VLOG(1) << homography_mat;
+ EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
+}
+
+TEST(Homography2DTest, HomographyGeneral) {
+ Mat x1(3, 4);
+ x1 << 0, 1, 0, 5,
+ 0, 0, 2, 3,
+ 1, 1, 1, 1;
+ Mat3 m;
+ m << 3, -1, 4,
+ 6, -2, -3,
+ 1, -3, 1;
+
+ Mat x2 = x1;
+ for (int i = 0; i < x2.cols(); ++i)
+ x2.col(i) = m * x1.col(i);
+
+ Mat3 homography_mat;
+ EXPECT_TRUE(Homography2DFromCorrespondencesLinear(x1, x2, &homography_mat));
+
+ VLOG(1) << "Mat Homography2D ";
+ VLOG(1) << homography_mat;
+ EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
+}
+
+TEST(Homography3DTest, RotationAndTranslationXYZ) {
+ Mat x1(4, 5);
+ x1 << 0, 0, 1, 5, 2,
+ 0, 1, 2, 3, 5,
+ 0, 2, 0, 1, 5,
+ 1, 1, 1, 1, 1;
+ Mat4 M;
+ M.setIdentity();
+ /*
+ M = AngleAxisd(45.0, Vector3f::UnitZ())
+ * AngleAxisd(25.0, Vector3f::UnitX())
+ * AngleAxisd(5.0, Vector3f::UnitZ());*/
+
+ // Rotation on x + translation
+ double angle = 45.0;
+ Mat4 rot;
+ rot << 1, 0, 0, 1,
+ 0, cos(angle), -sin(angle), 3,
+ 0, sin(angle), cos(angle), -2,
+ 0, 0, 0, 1;
+ M *= rot;
+ // Rotation on y
+ angle = 25.0;
+ rot << cos(angle), 0, sin(angle), 0,
+ 0, 1, 0, 0,
+ -sin(angle), 0, cos(angle), 0,
+ 0, 0, 0, 1;
+ M *= rot;
+ // Rotation on z
+ angle = 5.0;
+ rot << cos(angle), -sin(angle), 0, 0,
+ sin(angle), cos(angle), 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1;
+ M *= rot;
+ Mat x2 = x1;
+ for (int i = 0; i < x2.cols(); ++i) {
+ x2.col(i) = M * x1.col(i);
+ }
+
+ Mat4 homography_mat;
+ EXPECT_TRUE(Homography3DFromCorrespondencesLinear(x1, x2, &homography_mat));
+
+ VLOG(1) << "Mat Homography3D " << homography_mat;
+ VLOG(1) << "Mat GT " << M;
+ EXPECT_MATRIX_NEAR(homography_mat, M, 1e-8);
+}
+
+TEST(Homography3DTest, AffineGeneral) {
+ Mat x1(4, 5);
+ x1 << 0, 0, 1, 5, 2,
+ 0, 1, 2, 3, 5,
+ 0, 2, 0, 1, 5,
+ 1, 1, 1, 1, 1;
+ Mat4 m;
+ m << 3, -1, 4, 1,
+ 6, -2, -3, -6,
+ 1, 0, 1, 2,
+ 0, 0, 0, 1;
+
+ Mat x2 = x1;
+ for (int i = 0; i < x2.cols(); ++i) {
+ x2.col(i) = m * x1.col(i);
+ }
+
+ Mat4 homography_mat;
+ EXPECT_TRUE(Homography3DFromCorrespondencesLinear(x1, x2, &homography_mat));
+ VLOG(1) << "Mat Homography3D ";
+ VLOG(1) << homography_mat;
+ EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
+}
+
+TEST(Homography3DTest, HomographyGeneral) {
+ Mat x1(4, 5);
+ x1 << 0, 0, 1, 5, 2,
+ 0, 1, 2, 3, 5,
+ 0, 2, 0, 1, 5,
+ 1, 1, 1, 1, 1;
+ Mat4 m;
+ m << 3, -1, 4, 1,
+ 6, -2, -3, -6,
+ 1, 0, 1, 2,
+ -3, 1, 0, 1;
+
+ Mat x2 = x1;
+ for (int i = 0; i < x2.cols(); ++i) {
+ x2.col(i) = m * x1.col(i);
+ }
+
+ Mat4 homography_mat;
+ EXPECT_TRUE(Homography3DFromCorrespondencesLinear(x1, x2, &homography_mat));
+ VLOG(1) << "Mat Homography3D";
+ VLOG(1) << homography_mat;
+ EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
+}
+
+} // namespace
diff --git a/extern/libmv/libmv/multiview/nviewtriangulation_test.cc b/extern/libmv/libmv/multiview/nviewtriangulation_test.cc
new file mode 100644
index 00000000000..5a4d8499753
--- /dev/null
+++ b/extern/libmv/libmv/multiview/nviewtriangulation_test.cc
@@ -0,0 +1,94 @@
+// Copyright (c) 2009 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <iostream>
+
+#include "libmv/base/vector.h"
+#include "libmv/logging/logging.h"
+#include "libmv/multiview/nviewtriangulation.h"
+#include "libmv/multiview/projection.h"
+#include "libmv/multiview/test_data_sets.h"
+#include "libmv/numeric/numeric.h"
+#include "testing/testing.h"
+
+namespace {
+
+using namespace libmv;
+
+TEST(NViewTriangulate, FiveViews) {
+ int nviews = 5;
+ int npoints = 6;
+ NViewDataSet d = NRealisticCamerasFull(nviews, npoints);
+
+ // Collect P matrices together.
+ vector<Mat34> Ps(nviews);
+ for (int j = 0; j < nviews; ++j) {
+ Ps[j] = d.P(j);
+ }
+
+ for (int i = 0; i < npoints; ++i) {
+ // Collect the image of point i in each frame.
+ Mat2X xs(2, nviews);
+ for (int j = 0; j < nviews; ++j) {
+ xs.col(j) = d.x[j].col(i);
+ }
+ Vec4 X;
+ NViewTriangulate(xs, Ps, &X);
+
+ // Check reprojection error. Should be nearly zero.
+ for (int j = 0; j < nviews; ++j) {
+ Vec3 x_reprojected = Ps[j]*X;
+ x_reprojected /= x_reprojected(2);
+ double error = (x_reprojected.head(2) - xs.col(j)).norm();
+ EXPECT_NEAR(error, 0.0, 1e-9);
+ }
+ }
+}
+
+TEST(NViewTriangulateAlgebraic, FiveViews) {
+ int nviews = 5;
+ int npoints = 6;
+ NViewDataSet d = NRealisticCamerasFull(nviews, npoints);
+
+ // Collect P matrices together.
+ vector<Mat34> Ps(nviews);
+ for (int j = 0; j < nviews; ++j) {
+ Ps[j] = d.P(j);
+ }
+
+ for (int i = 0; i < npoints; ++i) {
+ // Collect the image of point i in each frame.
+ Mat2X xs(2, nviews);
+ for (int j = 0; j < nviews; ++j) {
+ xs.col(j) = d.x[j].col(i);
+ }
+ Vec4 X;
+ NViewTriangulate(xs, Ps, &X);
+
+ // Check reprojection error. Should be nearly zero.
+ for (int j = 0; j < nviews; ++j) {
+ Vec3 x_reprojected = Ps[j]*X;
+ x_reprojected /= x_reprojected(2);
+ double error = (x_reprojected.head<2>() - xs.col(j)).norm();
+ EXPECT_NEAR(error, 0.0, 1e-9);
+ }
+ }
+}
+} // namespace
diff --git a/extern/libmv/libmv/multiview/panography_kernel.cc b/extern/libmv/libmv/multiview/panography_kernel.cc
new file mode 100644
index 00000000000..8fdc9e79aed
--- /dev/null
+++ b/extern/libmv/libmv/multiview/panography_kernel.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2008, 2009 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/multiview/panography_kernel.h"
+#include "libmv/multiview/panography.h"
+
+namespace libmv {
+namespace panography {
+namespace kernel {
+
+void TwoPointSolver::Solve(const Mat &x1, const Mat &x2, vector<Mat3> *Hs) {
+ // Solve for the focal lengths.
+ vector<double> fs;
+ F_FromCorrespondance_2points(x1, x2, &fs);
+
+ // Then solve for the rotations and homographies.
+ Mat x1h, x2h;
+ EuclideanToHomogeneous(x1, &x1h);
+ EuclideanToHomogeneous(x2, &x2h);
+ for (int i = 0; i < fs.size(); ++i) {
+ Mat3 K1 = Mat3::Identity() * fs[i];
+ K1(2, 2) = 1.0;
+
+ Mat3 R;
+ GetR_FixedCameraCenter(x1h, x2h, fs[i], &R);
+ R /= R(2, 2);
+
+ (*Hs).push_back(K1 * R * K1.inverse());
+ }
+}
+
+} // namespace kernel
+} // namespace panography
+} // namespace libmv
diff --git a/extern/libmv/libmv/multiview/panography_kernel.h b/extern/libmv/libmv/multiview/panography_kernel.h
new file mode 100644
index 00000000000..a6adbd54b20
--- /dev/null
+++ b/extern/libmv/libmv/multiview/panography_kernel.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2009 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef LIBMV_MULTIVIEW_PANOGRAPHY_KERNEL_H
+#define LIBMV_MULTIVIEW_PANOGRAPHY_KERNEL_H
+
+#include "libmv/base/vector.h"
+#include "libmv/multiview/conditioning.h"
+#include "libmv/multiview/projection.h"
+#include "libmv/multiview/two_view_kernel.h"
+#include "libmv/multiview/homography_error.h"
+#include "libmv/numeric/numeric.h"
+
+namespace libmv {
+namespace panography {
+namespace kernel {
+
+struct TwoPointSolver {
+ enum { MINIMUM_SAMPLES = 2 };
+ static void Solve(const Mat &x1, const Mat &x2, vector<Mat3> *Hs);
+};
+
+typedef two_view::kernel::Kernel<
+ TwoPointSolver, homography::homography2D::AsymmetricError, Mat3>
+ UnnormalizedKernel;
+
+typedef two_view::kernel::Kernel<
+ two_view::kernel::NormalizedSolver<TwoPointSolver, UnnormalizerI>,
+ homography::homography2D::AsymmetricError,
+ Mat3>
+ Kernel;
+
+} // namespace kernel
+} // namespace panography
+} // namespace libmv
+
+#endif // LIBMV_MULTIVIEW_PANOGRAPHY_KERNEL_H
diff --git a/extern/libmv/libmv/multiview/panography_test.cc b/extern/libmv/libmv/multiview/panography_test.cc
new file mode 100644
index 00000000000..f6faf0f6022
--- /dev/null
+++ b/extern/libmv/libmv/multiview/panography_test.cc
@@ -0,0 +1,144 @@
+// Copyright (c) 2009 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/logging/logging.h"
+#include "libmv/multiview/panography.h"
+#include "libmv/multiview/panography_kernel.h"
+#include "libmv/multiview/projection.h"
+#include "libmv/numeric/numeric.h"
+#include "testing/testing.h"
+
+namespace libmv {
+namespace {
+
+TEST(Panography, PrintSomeSharedFocalEstimationValues) {
+ Mat x1(2, 2), x2(2, 2);
+ x1<< 158, 78,
+ 124, 113;
+ x2<< 300, 214,
+ 125, 114;
+
+ // Normalize data (set principal point 0,0 and image border to 1.0).
+ x1.block<1, 2>(0, 0) /= 320;
+ x1.block<1, 2>(1, 0) /= 240;
+ x2.block<1, 2>(0, 0) /= 320;
+ x2.block<1, 2>(1, 0) /= 240;
+ x1+=Mat2::Constant(0.5);
+ x2+=Mat2::Constant(0.5);
+
+ vector<double> fs;
+ F_FromCorrespondance_2points(x1, x2, &fs);
+
+ // Assert we found a valid solution.
+ EXPECT_EQ(1, fs.size());
+ EXPECT_NEAR(1.01667, fs[1], 1e-3);
+}
+
+TEST(Panography, GetR_FixedCameraCenterWithIdentity) {
+ Mat x1(3, 3);
+ x1 << 0.5, 0.6, 0.7,
+ 0.5, 0.5, 0.4,
+ 10.0, 10.0, 10.0;
+
+ Mat3 R;
+ GetR_FixedCameraCenter(x1, x1, 1.0, &R);
+ R /= R(2, 2);
+ EXPECT_MATRIX_NEAR(Mat3::Identity(), R, 1e-8);
+ LOG(INFO) << "R \n" << R;
+}
+
+TEST(Panography, Homography_GetR_Test_PitchY30) {
+ int n = 3;
+
+ Mat x1(3, n);
+ x1 << 0.5, 0.6, 0.7,
+ 0.5, 0.5, 0.4,
+ 10, 10, 10;
+
+ Mat x2 = x1;
+ const double alpha = 30.0 * M_PI / 180.0;
+ Mat3 rotY;
+ rotY << cos(alpha), 0, -sin(alpha),
+ 0, 1, 0,
+ sin(alpha), 0, cos(alpha);
+
+ for (int i = 0; i < n; ++i) {
+ x2.block<3, 1>(0, i) = rotY * x1.col(i);
+ }
+
+ Mat3 R;
+ GetR_FixedCameraCenter(x1, x2, 1.0, &R);
+
+ // Assert that residuals are small enough
+ for (int i = 0; i < n; ++i) {
+ Vec residuals = (R * x1.col(i)) - x2.col(i);
+ EXPECT_NEAR(0, residuals.norm(), 1e-6);
+ }
+
+ // Check that the rotation angle along Y is the expected one.
+ // Use the euler approximation to recover the angle.
+ double pitch_y = asin(R(2, 0)) * 180.0 / M_PI;
+ EXPECT_NEAR(30, pitch_y, 1e-4);
+}
+
+TEST(MinimalPanoramic, Real_Case_Kernel) {
+ const int n = 2;
+ Mat x1(2, n); // From image 0.jpg
+ x1<< 158, 78,
+ 124, 113;
+
+ Mat x2(2, n); // From image 3.jpg
+ x2<< 300, 214,
+ 125, 114;
+
+ Mat3 Ground_TruthHomography;
+ Ground_TruthHomography<< 1, 0.02, 129.83,
+ -0.02, 1.012, 0.07823,
+ 0, 0, 1;
+
+ vector<Mat3> Hs;
+
+ libmv::panography::kernel::TwoPointSolver::Solve(x1, x2, &Hs);
+
+ LOG(INFO) << "Got " << Hs.size() << " solutions.";
+ for (int j = 0; j < Hs.size(); ++j) {
+ Mat3 H = Hs[j];
+
+ EXPECT_MATRIX_NEAR(H, Ground_TruthHomography, 1e-1);
+
+ Mat x1h, x2h;
+ EuclideanToHomogeneous(x1, &x1h);
+ EuclideanToHomogeneous(x2, &x2h);
+
+ // Assert that residuals are small enough
+ for (int i = 0; i < n; ++i) {
+ Vec x1p = H * x1h.col(i);
+ Vec residuals = x1p/x1p(2) - x2h.col(i);
+ EXPECT_MATRIX_NEAR_ZERO(residuals, 1e-5);
+ }
+ }
+}
+
+} // namespace
+} // namespace libmv
+
+// TODO(pmoulon): Add a real test case based on images.
+// TODO(pmoulon): Add a check for the actual f value for the real images.
+// TODO(pmoulon): Add a test that has some inliers and outliers.
diff --git a/extern/libmv/libmv/multiview/projection_test.cc b/extern/libmv/libmv/multiview/projection_test.cc
new file mode 100644
index 00000000000..c060bfb0681
--- /dev/null
+++ b/extern/libmv/libmv/multiview/projection_test.cc
@@ -0,0 +1,115 @@
+// Copyright (c) 2007, 2008 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <iostream>
+
+#include "libmv/multiview/projection.h"
+#include "libmv/numeric/numeric.h"
+#include "testing/testing.h"
+
+namespace {
+using namespace libmv;
+
+TEST(Projection, P_From_KRt) {
+ Mat3 K, Kp;
+ K << 10, 1, 30,
+ 0, 20, 40,
+ 0, 0, 1;
+
+ Mat3 R, Rp;
+ R << 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1;
+
+ Vec3 t, tp;
+ t << 1, 2, 3;
+
+ Mat34 P;
+ P_From_KRt(K, R, t, &P);
+ KRt_From_P(P, &Kp, &Rp, &tp);
+
+ EXPECT_MATRIX_NEAR(K, Kp, 1e-8);
+ EXPECT_MATRIX_NEAR(R, Rp, 1e-8);
+ EXPECT_MATRIX_NEAR(t, tp, 1e-8);
+
+ // TODO(keir): Change the code to ensure det(R) == 1, which is not currently
+ // the case. Also add a test for that here.
+}
+
+Vec4 GetRandomPoint() {
+ Vec4 X;
+ X.setRandom();
+ X(3) = 1;
+ return X;
+}
+
+TEST(Projection, isInFrontOfCamera) {
+ Mat34 P;
+ P << 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0;
+
+ Vec4 X_front = GetRandomPoint();
+ Vec4 X_back = GetRandomPoint();
+ X_front(2) = 10; // Any point in the positive Z direction
+ // where Z > 1 is infront of the camera.
+ X_back(2) = -10; // Any point int he negative Z dirstaion
+ // is behind the camera.
+
+ bool res_front = isInFrontOfCamera(P, X_front);
+ bool res_back = isInFrontOfCamera(P, X_back);
+
+ EXPECT_EQ(true, res_front);
+ EXPECT_EQ(false, res_back);
+}
+
+TEST(AutoCalibration, ProjectionShiftPrincipalPoint) {
+ Mat34 P1, P2;
+ P1 << 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0;
+ P2 << 1, 0, 3, 0,
+ 0, 1, 4, 0,
+ 0, 0, 1, 0;
+ Mat34 P1_computed, P2_computed;
+ ProjectionShiftPrincipalPoint(P1, Vec2(0, 0), Vec2(3, 4), &P2_computed);
+ ProjectionShiftPrincipalPoint(P2, Vec2(3, 4), Vec2(0, 0), &P1_computed);
+
+ EXPECT_MATRIX_EQ(P1, P1_computed);
+ EXPECT_MATRIX_EQ(P2, P2_computed);
+}
+
+TEST(AutoCalibration, ProjectionChangeAspectRatio) {
+ Mat34 P1, P2;
+ P1 << 1, 0, 3, 0,
+ 0, 1, 4, 0,
+ 0, 0, 1, 0;
+ P2 << 1, 0, 3, 0,
+ 0, 2, 4, 0,
+ 0, 0, 1, 0;
+ Mat34 P1_computed, P2_computed;
+ ProjectionChangeAspectRatio(P1, Vec2(3, 4), 1, 2, &P2_computed);
+ ProjectionChangeAspectRatio(P2, Vec2(3, 4), 2, 1, &P1_computed);
+
+ EXPECT_MATRIX_EQ(P1, P1_computed);
+ EXPECT_MATRIX_EQ(P2, P2_computed);
+}
+
+} // namespace
diff --git a/extern/libmv/libmv/multiview/resection_test.cc b/extern/libmv/libmv/multiview/resection_test.cc
new file mode 100644
index 00000000000..368e2281cfa
--- /dev/null
+++ b/extern/libmv/libmv/multiview/resection_test.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2009 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <iostream>
+
+#include "libmv/multiview/projection.h"
+#include "libmv/multiview/resection.h"
+#include "libmv/multiview/test_data_sets.h"
+#include "libmv/numeric/numeric.h"
+#include "testing/testing.h"
+#include "libmv/logging/logging.h"
+
+namespace {
+
+using namespace libmv;
+using namespace libmv::resection;
+
+TEST(Resection, ThreeViews) {
+ int nviews = 5;
+ int npoints = 6;
+ NViewDataSet d = NRealisticCamerasFull(nviews, npoints);
+ for (int i = 0; i < nviews; ++i) {
+ Mat4X X(4, npoints);
+ X.block(0, 0, 3, npoints) = d.X;
+ X.row(3).setOnes();
+ const Mat2X &x = d.x[i];
+ Mat34 P;
+ Resection(x, X, &P);
+ Mat34 P_expected = d.P(i);
+
+ // Because the P matrices are homogeneous, it is necessary to be tricky
+ // about the scale factor to make them match.
+ P_expected *= 1/P_expected.array().abs().sum();
+ P *= 1/P.array().abs().sum();
+ if (!((P(0, 0) > 0 && P_expected(0, 0) > 0) ||
+ (P(0, 0) < 0 && P_expected(0, 0) < 0))) {
+ P *= -1;
+ }
+
+ EXPECT_MATRIX_NEAR(P_expected, P, 1e-9);
+ }
+}
+
+} // namespace
diff --git a/extern/libmv/libmv/multiview/test_data_sets.cc b/extern/libmv/libmv/multiview/test_data_sets.cc
new file mode 100644
index 00000000000..110bde6f762
--- /dev/null
+++ b/extern/libmv/libmv/multiview/test_data_sets.cc
@@ -0,0 +1,196 @@
+// Copyright (c) 2007, 2008 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/multiview/test_data_sets.h"
+
+#include <cmath>
+
+#include "libmv/numeric/numeric.h"
+#include "libmv/multiview/projection.h"
+#include "libmv/multiview/fundamental.h"
+
+namespace libmv {
+
+TwoViewDataSet TwoRealisticCameras(bool same_K) {
+ TwoViewDataSet d;
+
+ d.K1 << 320, 0, 160,
+ 0, 320, 120,
+ 0, 0, 1;
+ if (same_K) {
+ d.K2 = d.K1;
+ } else {
+ d.K2 << 360, 0, 170,
+ 0, 360, 110,
+ 0, 0, 1;
+ }
+ d.R1 = RotationAroundZ(-0.1);
+ d.R2 = RotationAroundX(-0.1);
+ d.t1 << 1, 1, 10;
+ d.t2 << -2, -1, 10;
+ P_From_KRt(d.K1, d.R1, d.t1, &d.P1);
+ P_From_KRt(d.K2, d.R2, d.t2, &d.P2);
+
+ FundamentalFromProjections(d.P1, d.P2, &d.F);
+
+ d.X.resize(3, 30);
+ d.X.setRandom();
+
+ Project(d.P1, d.X, &d.x1);
+ Project(d.P2, d.X, &d.x2);
+
+ return d;
+}
+
+nViewDatasetConfigator::nViewDatasetConfigator(int fx , int fy,
+ int cx, int cy,
+ double distance,
+ double jitter_amount) {
+ _fx = fx;
+ _fy = fy;
+ _cx = cx;
+ _cy = cy;
+ _dist = distance;
+ _jitter_amount = jitter_amount;
+}
+
+NViewDataSet NRealisticCamerasFull(int nviews, int npoints,
+ const nViewDatasetConfigator config) {
+ NViewDataSet d;
+ d.n = nviews;
+ d.K.resize(nviews);
+ d.R.resize(nviews);
+ d.t.resize(nviews);
+ d.C.resize(nviews);
+ d.x.resize(nviews);
+ d.x_ids.resize(nviews);
+
+ d.X.resize(3, npoints);
+ d.X.setRandom();
+ d.X *= 0.6;
+
+ Vecu all_point_ids(npoints);
+ for (size_t j = 0; j < npoints; ++j)
+ all_point_ids[j] = j;
+
+ for (size_t i = 0; i < nviews; ++i) {
+ Vec3 camera_center, t, jitter, lookdir;
+
+ double theta = i * 2 * M_PI / nviews;
+ camera_center << sin(theta), 0.0, cos(theta);
+ camera_center *= config._dist;
+ d.C[i] = camera_center;
+
+ jitter.setRandom();
+ jitter *= config._jitter_amount / camera_center.norm();
+ lookdir = -camera_center + jitter;
+
+ d.K[i] << config._fx, 0, config._cx,
+ 0, config._fy, config._cy,
+ 0, 0, 1;
+ d.R[i] = LookAt(lookdir);
+ d.t[i] = -d.R[i] * camera_center;
+ d.x[i] = Project(d.P(i), d.X);
+ d.x_ids[i] = all_point_ids;
+ }
+ return d;
+}
+
+
+NViewDataSet NRealisticCamerasSparse(int nviews, int npoints,
+ float view_ratio, unsigned min_projections,
+ const nViewDatasetConfigator config) {
+ assert(view_ratio <= 1.0);
+ assert(view_ratio > 0.0);
+ assert(min_projections <= npoints);
+ NViewDataSet d;
+ d.n = nviews;
+ d.K.resize(nviews);
+ d.R.resize(nviews);
+ d.t.resize(nviews);
+ d.C.resize(nviews);
+ d.x.resize(nviews);
+ d.x_ids.resize(nviews);
+
+ d.X.resize(3, npoints);
+ d.X.setRandom();
+ d.X *= 0.6;
+
+ Mat visibility(nviews, npoints);
+ visibility.setZero();
+ Mat randoms(nviews, npoints);
+ randoms.setRandom();
+ randoms = (randoms.array() + 1)/2.0;
+ unsigned num_visibles = 0;
+ for (size_t i = 0; i < nviews; ++i) {
+ num_visibles = 0;
+ for (size_t j = 0; j < npoints; j++) {
+ if (randoms(i, j) <= view_ratio) {
+ visibility(i, j) = true;
+ num_visibles++;
+ }
+ }
+ if (num_visibles < min_projections) {
+ unsigned num_projections_to_add = min_projections - num_visibles;
+ for (size_t j = 0; j < npoints && num_projections_to_add > 0; ++j) {
+ if (!visibility(i, j)) {
+ num_projections_to_add--;
+ }
+ }
+ num_visibles += num_projections_to_add;
+ }
+ d.x_ids[i].resize(num_visibles);
+ d.x[i].resize(2, num_visibles);
+ }
+
+ size_t j_visible = 0;
+ Vec3 X;
+ for (size_t i = 0; i < nviews; ++i) {
+ Vec3 camera_center, t, jitter, lookdir;
+
+ double theta = i * 2 * M_PI / nviews;
+ camera_center << sin(theta), 0.0, cos(theta);
+ camera_center *= config._dist;
+ d.C[i] = camera_center;
+
+ jitter.setRandom();
+ jitter *= config._jitter_amount / camera_center.norm();
+ lookdir = -camera_center + jitter;
+
+ d.K[i] << config._fx, 0, config._cx,
+ 0, config._fy, config._cy,
+ 0, 0, 1;
+ d.R[i] = LookAt(lookdir);
+ d.t[i] = -d.R[i] * camera_center;
+ j_visible = 0;
+ for (size_t j = 0; j < npoints; j++) {
+ if (visibility(i, j)) {
+ X = d.X.col(j);
+ d.x[i].col(j_visible) = Project(d.P(i), X);
+ d.x_ids[i][j_visible] = j;
+ j_visible++;
+ }
+ }
+ }
+ return d;
+}
+
+
+} // namespace libmv
diff --git a/extern/libmv/libmv/multiview/test_data_sets.h b/extern/libmv/libmv/multiview/test_data_sets.h
new file mode 100644
index 00000000000..cf01663ca02
--- /dev/null
+++ b/extern/libmv/libmv/multiview/test_data_sets.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2007, 2008 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef LIBMV_MULTIVIEW_TEST_DATA_SETS_H_
+#define LIBMV_MULTIVIEW_TEST_DATA_SETS_H_
+
+#include "libmv/base/vector.h"
+#include "libmv/multiview/fundamental.h"
+#include "libmv/multiview/projection.h"
+#include "libmv/numeric/numeric.h"
+
+namespace libmv {
+
+struct TwoViewDataSet {
+ Mat3 K1, K2; // Internal parameters.
+ Mat3 R1, R2; // Rotation.
+ Vec3 t1, t2; // Translation.
+ Mat34 P1, P2; // Projection matrix, P = K(R|t)
+ Mat3 F; // Fundamental matrix.
+ Mat3X X; // 3D points.
+ Mat2X x1, x2; // Projected points.
+};
+
+// Two cameras at (-1,-1,-10) and (2,1,-10) looking approximately towards z+.
+TwoViewDataSet TwoRealisticCameras(bool same_K = false);
+
+// An N-view metric dataset . An important difference between this
+// and the other reconstruction data types is that all points are seen by all
+// cameras.
+struct NViewDataSet {
+ vector<Mat3> K; // Internal parameters (fx, fy, etc).
+ vector<Mat3> R; // Rotation.
+ vector<Vec3> t; // Translation.
+ vector<Vec3> C; // Camera centers.
+ Mat3X X; // 3D points.
+ vector<Mat2X> x; // Projected points; may have noise added.
+ vector<Vecu> x_ids; // Indexes of points corresponding to the projections
+
+ int n; // Actual number of cameras.
+
+ Mat34 P(int i) {
+ assert(i < n);
+ return K[i] * HStack(R[i], t[i]);
+ }
+ Mat3 F(int i, int j) {
+ Mat3 F_;
+ FundamentalFromProjections(P(i), P(j), &F_);
+ return F_;
+ }
+ void Reproject() {
+ for (int i = 0; i < n; ++i) {
+ x[i] = Project(P(i), X);
+ }
+ }
+ // TODO(keir): Add gaussian jitter functions.
+};
+
+struct nViewDatasetConfigator {
+ /// Internal camera parameters
+ int _fx;
+ int _fy;
+ int _cx;
+ int _cy;
+
+ /// Camera random position parameters
+ double _dist;
+ double _jitter_amount;
+
+ nViewDatasetConfigator(int fx = 1000, int fy = 1000,
+ int cx = 500, int cy = 500,
+ double distance = 1.5,
+ double jitter_amount = 0.01);
+};
+
+NViewDataSet NRealisticCamerasFull(int nviews, int npoints,
+ const nViewDatasetConfigator
+ config = nViewDatasetConfigator());
+
+// Generates sparse projections (not all points are projected)
+NViewDataSet NRealisticCamerasSparse(int nviews, int npoints,
+ float view_ratio = 0.6,
+ unsigned min_projections = 3,
+ const nViewDatasetConfigator
+ config = nViewDatasetConfigator());
+
+} // namespace libmv
+
+#endif // LIBMV_MULTIVIEW_TEST_DATA_SETS_H_
diff --git a/extern/libmv/libmv/multiview/triangulation_test.cc b/extern/libmv/libmv/multiview/triangulation_test.cc
new file mode 100644
index 00000000000..66d1ee25a62
--- /dev/null
+++ b/extern/libmv/libmv/multiview/triangulation_test.cc
@@ -0,0 +1,47 @@
+// Copyright (c) 2007, 2008 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <iostream>
+
+#include "libmv/multiview/triangulation.h"
+#include "libmv/multiview/fundamental.h"
+#include "libmv/multiview/projection.h"
+#include "libmv/multiview/test_data_sets.h"
+#include "libmv/numeric/numeric.h"
+#include "testing/testing.h"
+
+namespace {
+using namespace libmv;
+
+TEST(Triangulation, TriangulateDLT) {
+ TwoViewDataSet d = TwoRealisticCameras();
+
+ for (int i = 0; i < d.X.cols(); ++i) {
+ Vec2 x1, x2;
+ MatrixColumn(d.x1, i, &x1);
+ MatrixColumn(d.x2, i, &x2);
+ Vec3 X_estimated, X_gt;
+ MatrixColumn(d.X, i, &X_gt);
+ TriangulateDLT(d.P1, x1, d.P2, x2, &X_estimated);
+ EXPECT_NEAR(0, DistanceLInfinity(X_estimated, X_gt), 1e-8);
+ }
+}
+
+} // namespace
diff --git a/extern/libmv/libmv/multiview/two_view_kernel.h b/extern/libmv/libmv/multiview/two_view_kernel.h
new file mode 100644
index 00000000000..7af0ed5ddab
--- /dev/null
+++ b/extern/libmv/libmv/multiview/two_view_kernel.h
@@ -0,0 +1,137 @@
+// Copyright (c) 2009 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef LIBMV_MULTIVIEW_TWO_VIEW_KERNEL_H_
+#define LIBMV_MULTIVIEW_TWO_VIEW_KERNEL_H_
+
+#include "libmv/base/vector.h"
+#include "libmv/logging/logging.h"
+#include "libmv/multiview/conditioning.h"
+#include "libmv/numeric/numeric.h"
+
+namespace libmv {
+namespace two_view {
+namespace kernel {
+
+template<typename Solver, typename Unnormalizer>
+struct NormalizedSolver {
+ enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES };
+ static void Solve(const Mat &x1, const Mat &x2, vector<Mat3> *models) {
+ assert(2 == x1.rows());
+ assert(MINIMUM_SAMPLES <= x1.cols());
+ assert(x1.rows() == x2.rows());
+ assert(x1.cols() == x2.cols());
+
+ // Normalize the data.
+ Mat3 T1, T2;
+ Mat x1_normalized, x2_normalized;
+ NormalizePoints(x1, &x1_normalized, &T1);
+ NormalizePoints(x2, &x2_normalized, &T2);
+
+ Solver::Solve(x1_normalized, x2_normalized, models);
+
+ for (int i = 0; i < models->size(); ++i) {
+ Unnormalizer::Unnormalize(T1, T2, &(*models)[i]);
+ }
+ }
+};
+
+template<typename Solver, typename Unnormalizer>
+struct IsotropicNormalizedSolver {
+ enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES };
+ static void Solve(const Mat &x1, const Mat &x2, vector<Mat3> *models) {
+ assert(2 == x1.rows());
+ assert(MINIMUM_SAMPLES <= x1.cols());
+ assert(x1.rows() == x2.rows());
+ assert(x1.cols() == x2.cols());
+
+ // Normalize the data.
+ Mat3 T1, T2;
+ Mat x1_normalized, x2_normalized;
+ NormalizeIsotropicPoints(x1, &x1_normalized, &T1);
+ NormalizeIsotropicPoints(x2, &x2_normalized, &T2);
+
+ Solver::Solve(x1_normalized, x2_normalized, models);
+
+ for (int i = 0; i < models->size(); ++i) {
+ Unnormalizer::Unnormalize(T1, T2, &(*models)[i]);
+ }
+ }
+};
+// This is one example (targeted at solvers that operate on correspondences
+// between two views) that shows the "kernel" part of a robust fitting
+// problem:
+//
+// 1. The model; Mat3 in the case of the F or H matrix.
+// 2. The minimum number of samples needed to fit; 7 or 8 (or 4).
+// 3. A way to convert samples to a model.
+// 4. A way to convert a sample and a model to an error.
+//
+// Of particular note is that the kernel does not expose what the samples are.
+// All the robust fitting algorithm sees is that there is some number of
+// samples; it is able to fit subsets of them (via the kernel) and check their
+// error, but can never access the samples themselves.
+//
+// The Kernel objects must follow the following concept so that the robust
+// fitting alogrithm can fit this type of relation:
+//
+// 1. Kernel::Model
+// 2. Kernel::MINIMUM_SAMPLES
+// 3. Kernel::Fit(vector<int>, vector<Kernel::Model> *)
+// 4. Kernel::Error(int, Model) -> error
+//
+// The fit routine must not clear existing entries in the vector of models; it
+// should append new solutions to the end.
+template<typename SolverArg,
+ typename ErrorArg,
+ typename ModelArg = Mat3>
+class Kernel {
+ public:
+ Kernel(const Mat &x1, const Mat &x2) : x1_(x1), x2_(x2) {}
+ typedef SolverArg Solver;
+ typedef ModelArg Model;
+ enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES };
+ void Fit(const vector<int> &samples, vector<Model> *models) const {
+ Mat x1 = ExtractColumns(x1_, samples);
+ Mat x2 = ExtractColumns(x2_, samples);
+ Solver::Solve(x1, x2, models);
+ }
+ double Error(int sample, const Model &model) const {
+ return ErrorArg::Error(model,
+ static_cast<Vec>(x1_.col(sample)),
+ static_cast<Vec>(x2_.col(sample)));
+ }
+ int NumSamples() const {
+ return x1_.cols();
+ }
+ static void Solve(const Mat &x1, const Mat &x2, vector<Model> *models) {
+ // By offering this, Kernel types can be passed to templates.
+ Solver::Solve(x1, x2, models);
+ }
+ protected:
+ const Mat &x1_;
+ const Mat &x2_;
+};
+
+} // namespace kernel
+} // namespace two_view
+} // namespace libmv
+
+#endif // LIBMV_MULTIVIEW_TWO_VIEW_KERNEL_H_
diff --git a/extern/libmv/libmv/numeric/dogleg_test.cc b/extern/libmv/libmv/numeric/dogleg_test.cc
new file mode 100644
index 00000000000..90c46c31672
--- /dev/null
+++ b/extern/libmv/libmv/numeric/dogleg_test.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2009 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "testing/testing.h"
+#include "libmv/numeric/dogleg.h"
+
+using namespace libmv;
+
+namespace {
+
+class F {
+ public:
+ typedef Vec4 FMatrixType;
+ typedef Vec3 XMatrixType;
+ Vec4 operator()(const Vec3 &x) const {
+ double x1 = x.x() - 2;
+ double y1 = x.y() - 5;
+ double z1 = x.z();
+ Vec4 fx; fx << x1*x1 + z1*z1,
+ y1*y1 + z1*z1,
+ z1*z1,
+ x1*x1;
+ return fx;
+ }
+};
+
+TEST(Dogleg, SimpleCase) {
+ Vec3 x; x << 0.76026643, -30.01799744, 0.55192142;
+ F f;
+ Dogleg<F>::SolverParameters params;
+ Dogleg<F> lm(f);
+ /* TODO(sergey): Better error handling. */
+ /* Dogleg<F>::Results results = */ lm.minimize(params, &x);
+ Vec3 expected_min_x; expected_min_x << 2, 5, 0;
+
+ EXPECT_MATRIX_NEAR(expected_min_x, x, 1e-5);
+}
+
+// Example 3.2 from [1]; page 11 of the pdf, 20 of the document. This is a
+// tricky problem because of the singluar Jacobian near the origin.
+class F32 {
+ public:
+ typedef Vec2 FMatrixType;
+ typedef Vec2 XMatrixType;
+ Vec2 operator()(const Vec2 &x) const {
+ double x1 = x(0);
+ double x2 = 10*x(0)/(x(0) + 0.1) + 2*x(1)*x(1);
+ Vec2 fx; fx << x1, x2;
+ return fx;
+ }
+};
+
+class JF32 {
+ public:
+ JF32(const F32 &f) { (void) f; }
+ Mat2 operator()(const Vec2 &x) {
+ Mat2 J; J << 1, 0,
+ 1./pow(x(0) + 0.1, 2), 4*x(1)*x(1);
+ return J;
+ }
+};
+
+// TODO(keir): Re-enable this when the dogleg code properly handles singular
+// normal equations.
+/*
+TEST(Dogleg, Example32) {
+ Vec2 x; x << 3, 1;
+ F32 f;
+ CheckJacobian<F32, JF32>(f, x);
+ Dogleg<F32, JF32> dogleg(f);
+ Dogleg<F32, JF32>::Results results = dogleg.minimize(&x);
+ Vec2 expected_min_x; expected_min_x << 0, 0;
+
+ EXPECT_MATRIX_NEAR(expected_min_x, x, 1e-5);
+}
+*/
+
+} // namespace
diff --git a/extern/libmv/libmv/numeric/function_derivative_test.cc b/extern/libmv/libmv/numeric/function_derivative_test.cc
new file mode 100644
index 00000000000..8d976d3e9a0
--- /dev/null
+++ b/extern/libmv/libmv/numeric/function_derivative_test.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2007, 2008, 2009 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "testing/testing.h"
+#include "libmv/numeric/numeric.h"
+#include "libmv/numeric/function_derivative.h"
+
+using namespace libmv;
+
+namespace {
+
+class F {
+ public:
+ typedef Vec2 FMatrixType;
+ typedef Vec3 XMatrixType;
+ Vec2 operator()(const Vec3 &x) const {
+ Vec2 fx;
+ fx << 0.19*x(0) + 0.19*x(1)*x(1) + x(2),
+ 3*sin(x(0)) + 2*cos(x(1));
+ return fx;
+ }
+ Mat23 J(const Vec3 &x) const {
+ Mat23 jacobian;
+ jacobian << 0.19, 2*0.19*x(1), 1.0,
+ 3*cos(x(0)), -2*sin(x(1)), 0;
+ return jacobian;
+ }
+};
+
+TEST(FunctionDerivative, SimpleCase) {
+ Vec3 x; x << 0.76026643, 0.01799744, 0.55192142;
+ F f;
+ NumericJacobian<F, CENTRAL> J(f);
+ EXPECT_MATRIX_NEAR(f.J(x), J(x), 1e-8);
+ NumericJacobian<F, FORWARD> J_forward(f);
+ // Forward difference is very inaccurate.
+ EXPECT_MATRIX_NEAR(f.J(x), J_forward(x), 1e-5);
+}
+
+} // namespace
diff --git a/extern/libmv/libmv/numeric/levenberg_marquardt_test.cc b/extern/libmv/libmv/numeric/levenberg_marquardt_test.cc
new file mode 100644
index 00000000000..fc3f9ebbb29
--- /dev/null
+++ b/extern/libmv/libmv/numeric/levenberg_marquardt_test.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2007, 2008, 2009 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "testing/testing.h"
+#include "libmv/numeric/levenberg_marquardt.h"
+
+using namespace libmv;
+
+namespace {
+
+class F {
+ public:
+ typedef Vec4 FMatrixType;
+ typedef Vec3 XMatrixType;
+ Vec4 operator()(const Vec3 &x) const {
+ double x1 = x.x() - 2;
+ double y1 = x.y() - 5;
+ double z1 = x.z();
+ Vec4 fx; fx << x1*x1 + z1*z1,
+ y1*y1 + z1*z1,
+ z1*z1,
+ x1*x1;
+ return fx;
+ }
+};
+
+TEST(LevenbergMarquardt, SimpleCase) {
+ Vec3 x(0.76026643, -30.01799744, 0.55192142);
+ F f;
+ LevenbergMarquardt<F>::SolverParameters params;
+ LevenbergMarquardt<F> lm(f);
+ /* TODO(sergey): Better error handling. */
+ /* LevenbergMarquardt<F>::Results results = */ lm.minimize(params, &x);
+ Vec3 expected_min_x(2, 5, 0);
+
+ EXPECT_MATRIX_NEAR(expected_min_x, x, 1e-5);
+}
+
+} // namespace
diff --git a/extern/libmv/libmv/numeric/numeric_test.cc b/extern/libmv/libmv/numeric/numeric_test.cc
new file mode 100644
index 00000000000..0cdfaf33ab2
--- /dev/null
+++ b/extern/libmv/libmv/numeric/numeric_test.cc
@@ -0,0 +1,439 @@
+// Copyright (c) 2007, 2008 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/numeric/numeric.h"
+#include "testing/testing.h"
+
+using namespace libmv;
+
+namespace {
+
+TEST(Numeric, DynamicSizedNullspace) {
+ Mat A(3, 4);
+ A << 0.76026643, 0.01799744, 0.55192142, 0.8699745,
+ 0.42016166, 0.97863392, 0.33711682, 0.14479271,
+ 0.51016811, 0.66528302, 0.54395496, 0.57794893;
+ Vec x;
+ double s = Nullspace(&A, &x);
+ EXPECT_NEAR(0.0, s, 1e-15);
+ EXPECT_NEAR(0.0, (A * x).norm(), 1e-15);
+ EXPECT_NEAR(1.0, x.norm(), 1e-15);
+}
+
+TEST(Numeric, FixedSizeMatrixNullspace) {
+ Mat34 A;
+ A << 0.76026643, 0.01799744, 0.55192142, 0.8699745,
+ 0.42016166, 0.97863392, 0.33711682, 0.14479271,
+ 0.51016811, 0.66528302, 0.54395496, 0.57794893;
+ Vec x;
+ double s = Nullspace(&A, &x);
+ EXPECT_NEAR(0.0, s, 1e-15);
+ EXPECT_NEAR(0.0, (A * x).norm(), 1e-15);
+ EXPECT_NEAR(1.0, x.norm(), 1e-15);
+}
+
+TEST(Numeric, NullspaceMatchesLapackSVD) {
+ Mat43 A;
+ A << 0.76026643, 0.01799744, 0.55192142,
+ 0.8699745, 0.42016166, 0.97863392,
+ 0.33711682, 0.14479271, 0.51016811,
+ 0.66528302, 0.54395496, 0.57794893;
+ Vec x;
+ double s = Nullspace(&A, &x);
+ EXPECT_NEAR(1.0, x.norm(), 1e-15);
+ EXPECT_NEAR(0.206694992663, s, 1e-9);
+ EXPECT_NEAR(0.206694992663, (A * x).norm(), 1e-9);
+
+ EXPECT_NEAR(-0.64999717, x(0), 1e-8);
+ EXPECT_NEAR(-0.18452646, x(1), 1e-8);
+ EXPECT_NEAR(0.7371931, x(2), 1e-8);
+}
+
+TEST(Numeric, Nullspace2) {
+ Mat43 A;
+ A << 0.76026643, 0.01799744, 0.55192142,
+ 0.8699745, 0.42016166, 0.97863392,
+ 0.33711682, 0.14479271, 0.51016811,
+ 0.66528302, 0.54395496, 0.57794893;
+ Vec3 x1, x2;
+ double s = Nullspace2(&A, &x1, &x2);
+ EXPECT_NEAR(1.0, x1.norm(), 1e-15);
+ EXPECT_NEAR(0.206694992663, s, 1e-9);
+ EXPECT_NEAR(0.206694992663, (A * x1).norm(), 1e-9);
+
+ EXPECT_NEAR(-0.64999717, x1(0), 1e-8);
+ EXPECT_NEAR(-0.18452646, x1(1), 1e-8);
+ EXPECT_NEAR( 0.7371931, x1(2), 1e-8);
+
+ if (x2(0) < 0) {
+ x2 *= -1;
+ }
+ EXPECT_NEAR( 0.34679618, x2(0), 1e-8);
+ EXPECT_NEAR(-0.93519689, x2(1), 1e-8);
+ EXPECT_NEAR( 0.07168809, x2(2), 1e-8);
+}
+
+TEST(Numeric, TinyMatrixSquareTranspose) {
+ Mat2 A;
+ A << 1.0, 2.0, 3.0, 4.0;
+ libmv::TransposeInPlace(&A);
+ EXPECT_EQ(1.0, A(0, 0));
+ EXPECT_EQ(3.0, A(0, 1));
+ EXPECT_EQ(2.0, A(1, 0));
+ EXPECT_EQ(4.0, A(1, 1));
+}
+
+TEST(Numeric, NormalizeL1) {
+ Vec2 x;
+ x << 1, 2;
+ double l1 = NormalizeL1(&x);
+ EXPECT_DOUBLE_EQ(3., l1);
+ EXPECT_DOUBLE_EQ(1./3., x(0));
+ EXPECT_DOUBLE_EQ(2./3., x(1));
+}
+
+TEST(Numeric, NormalizeL2) {
+ Vec2 x;
+ x << 1, 2;
+ double l2 = NormalizeL2(&x);
+ EXPECT_DOUBLE_EQ(sqrt(5.0), l2);
+ EXPECT_DOUBLE_EQ(1./sqrt(5.), x(0));
+ EXPECT_DOUBLE_EQ(2./sqrt(5.), x(1));
+}
+
+TEST(Numeric, Diag) {
+ Vec x(2);
+ x << 1, 2;
+ Mat D = Diag(x);
+ EXPECT_EQ(1, D(0, 0));
+ EXPECT_EQ(0, D(0, 1));
+ EXPECT_EQ(0, D(1, 0));
+ EXPECT_EQ(2, D(1, 1));
+}
+
+TEST(Numeric, Determinant) {
+ Mat A(2, 2);
+ A << 1, 2,
+ -1, 3;
+ double detA = A.determinant();
+ EXPECT_NEAR(5, detA, 1e-8);
+
+ Mat B(4, 4);
+ B << 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11,
+ 12, 13, 14, 15;
+ double detB = B.determinant();
+ EXPECT_NEAR(0, detB, 1e-8);
+
+ Mat3 C;
+ C << 0, 1, 2,
+ 3, 4, 5,
+ 6, 7, 1;
+ double detC = C.determinant();
+ EXPECT_NEAR(21, detC, 1e-8);
+}
+
+TEST(Numeric, Inverse) {
+ Mat A(2, 2), A1;
+ A << 1, 2,
+ -1, 3;
+ Mat I = A * A.inverse();
+
+ EXPECT_NEAR(1, I(0, 0), 1e-8);
+ EXPECT_NEAR(0, I(0, 1), 1e-8);
+ EXPECT_NEAR(0, I(1, 0), 1e-8);
+ EXPECT_NEAR(1, I(1, 1), 1e-8);
+
+ Mat B(4, 4), B1;
+ B << 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 2, 11,
+ 12, 13, 14, 4;
+ Mat I2 = B * B.inverse();
+ EXPECT_NEAR(1, I2(0, 0), 1e-8);
+ EXPECT_NEAR(0, I2(0, 1), 1e-8);
+ EXPECT_NEAR(0, I2(0, 2), 1e-8);
+ EXPECT_NEAR(0, I2(1, 0), 1e-8);
+ EXPECT_NEAR(1, I2(1, 1), 1e-8);
+ EXPECT_NEAR(0, I2(1, 2), 1e-8);
+ EXPECT_NEAR(0, I2(2, 0), 1e-8);
+ EXPECT_NEAR(0, I2(2, 1), 1e-8);
+ EXPECT_NEAR(1, I2(2, 2), 1e-8);
+}
+
+TEST(Numeric, MeanAndVarianceAlongRows) {
+ int n = 4;
+ Mat points(2, n);
+ points << 0, 0, 1, 1,
+ 0, 2, 1, 3;
+
+ Vec mean, variance;
+ MeanAndVarianceAlongRows(points, &mean, &variance);
+
+ EXPECT_NEAR(0.5, mean(0), 1e-8);
+ EXPECT_NEAR(1.5, mean(1), 1e-8);
+ EXPECT_NEAR(0.25, variance(0), 1e-8);
+ EXPECT_NEAR(1.25, variance(1), 1e-8);
+}
+
+TEST(Numeric, HorizontalStack) {
+ Mat x(2, 1), y(2, 1), z;
+ x << 1, 2;
+ y << 3, 4;
+
+ HorizontalStack(x, y, &z);
+
+ EXPECT_EQ(2, z.cols());
+ EXPECT_EQ(2, z.rows());
+ EXPECT_EQ(1, z(0, 0));
+ EXPECT_EQ(2, z(1, 0));
+ EXPECT_EQ(3, z(0, 1));
+ EXPECT_EQ(4, z(1, 1));
+}
+
+TEST(Numeric, HStack) {
+ Mat x(2, 1), y(2, 1), z(2, 2);
+ x << 1, 2;
+ y << 3, 4;
+ z << 1, 3,
+ 2, 4;
+ Vec2 xC = x, yC = y;
+
+ Mat2 xy = HStack(x, y);
+ EXPECT_MATRIX_EQ(z, xy);
+
+ EXPECT_MATRIX_EQ(z, HStack(x, y));
+ EXPECT_MATRIX_EQ(z, HStack(x, yC));
+ EXPECT_MATRIX_EQ(z, HStack(xC, y));
+ EXPECT_MATRIX_EQ(z, HStack(xC, yC));
+}
+
+// TODO(keir): Need some way of verifying that the compile time types of the
+// resulting stacked matrices properly propagate the fixed dimensions.
+TEST(Numeric, VStack) {
+ Mat x(2, 2), y(2, 2), z(4, 2);
+ x << 1, 2,
+ 3, 4;
+ y << 10, 20,
+ 30, 40;
+ z << 1, 2,
+ 3, 4,
+ 10, 20,
+ 30, 40;
+ Mat2 xC = x, yC = y;
+
+ Mat xy = VStack(x, y);
+ EXPECT_MATRIX_EQ(z, xy);
+
+ EXPECT_MATRIX_EQ(z, VStack(x, y));
+ EXPECT_MATRIX_EQ(z, VStack(x, yC));
+ EXPECT_MATRIX_EQ(z, VStack(xC, y));
+ EXPECT_MATRIX_EQ(z, VStack(xC, yC));
+}
+
+TEST(Numeric, VerticalStack) {
+ Mat x(1, 2), y(1, 2), z;
+ x << 1, 2;
+ y << 3, 4;
+ VerticalStack(x, y, &z);
+
+ EXPECT_EQ(2, z.cols());
+ EXPECT_EQ(2, z.rows());
+ EXPECT_EQ(1, z(0, 0));
+ EXPECT_EQ(2, z(0, 1));
+ EXPECT_EQ(3, z(1, 0));
+ EXPECT_EQ(4, z(1, 1));
+}
+
+TEST(Numeric, CrossProduct) {
+ Vec3 x, y, z;
+ x << 1, 0, 0;
+ y << 0, 1, 0;
+ z << 0, 0, 1;
+ Vec3 xy = CrossProduct(x, y);
+ Vec3 yz = CrossProduct(y, z);
+ Vec3 zx = CrossProduct(z, x);
+ EXPECT_NEAR(0, DistanceLInfinity(xy, z), 1e-8);
+ EXPECT_NEAR(0, DistanceLInfinity(yz, x), 1e-8);
+ EXPECT_NEAR(0, DistanceLInfinity(zx, y), 1e-8);
+}
+
+TEST(Numeric, CrossProductMatrix) {
+ Vec3 x, y;
+ x << 1, 2, 3;
+ y << 2, 3, 4;
+ Vec3 xy = CrossProduct(x, y);
+ Vec3 yx = CrossProduct(y, x);
+ Mat3 X = CrossProductMatrix(x);
+ Vec3 Xy, Xty;
+ Xy = X * y;
+ Xty = X.transpose() * y;
+ EXPECT_NEAR(0, DistanceLInfinity(xy, Xy), 1e-8);
+ EXPECT_NEAR(0, DistanceLInfinity(yx, Xty), 1e-8);
+}
+
+TEST(Numeric, MatrixColumn) {
+ Mat A2(2, 3);
+ Vec2 v2;
+ A2 << 1, 2, 3,
+ 4, 5, 6;
+ MatrixColumn(A2, 1, &v2);
+ EXPECT_EQ(2, v2(0));
+ EXPECT_EQ(5, v2(1));
+
+ Mat A3(3, 3);
+ Vec3 v3;
+ A3 << 1, 2, 3,
+ 4, 5, 6,
+ 7, 8, 9;
+ MatrixColumn(A3, 1, &v3);
+ EXPECT_EQ(2, v3(0));
+ EXPECT_EQ(5, v3(1));
+ EXPECT_EQ(8, v3(2));
+
+ Mat A4(4, 3);
+ Vec4 v4;
+ A4 << 1, 2, 3,
+ 4, 5, 6,
+ 7, 8, 9,
+ 10, 11, 12;
+ MatrixColumn(A4, 1, &v4);
+ EXPECT_EQ( 2, v4(0));
+ EXPECT_EQ( 5, v4(1));
+ EXPECT_EQ( 8, v4(2));
+ EXPECT_EQ(11, v4(3));
+}
+
+// This used to give a compile error with FLENS.
+TEST(Numeric, TinyMatrixView) {
+ Mat34 P;
+ Mat K = P.block(0, 0, 3, 3);
+}
+
+// This gives a compile error.
+TEST(Numeric, Mat3MatProduct) {
+ Mat3 A;
+ Mat3 B;
+ Mat C = A * B;
+}
+
+// This gives a compile error.
+TEST(Numeric, Vec3Negative) {
+ Vec3 y; y << 1, 2, 3;
+ Vec3 x = -y;
+ EXPECT_EQ(-1, x(0));
+ EXPECT_EQ(-2, x(1));
+ EXPECT_EQ(-3, x(2));
+}
+
+// This gives a compile error.
+TEST(Numeric, Vec3VecInteroperability) {
+ Vec y(3);
+ y << 1, 2, 3;
+ Vec3 x = y + y;
+ EXPECT_EQ(2, x(0));
+ EXPECT_EQ(4, x(1));
+ EXPECT_EQ(6, x(2));
+}
+
+// This segfaults inside lapack.
+TEST(Numeric, DeterminantLU7) {
+ Mat A(5, 5);
+ A << 1, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 1;
+ EXPECT_NEAR(1, A.determinant(), 1e-8);
+}
+
+// This segfaults inside lapack.
+TEST(Numeric, DeterminantLU) {
+ Mat A(2, 2);
+ A << 1, 2,
+ -1, 3;
+ EXPECT_NEAR(5, A.determinant(), 1e-8);
+}
+
+// This does unexpected things.
+// Keir: Not with eigen2!
+TEST(Numeric, InplaceProduct) {
+ Mat2 K, S;
+ K << 1, 0,
+ 0, 1;
+ S << 1, 0,
+ 0, 1;
+ K = K * S;
+ EXPECT_MATRIX_NEAR(Mat2::Identity(), K, 1e-8);
+}
+
+TEST(Numeric, ExtractColumns) {
+ Mat2X A(2, 5);
+ A << 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, 10;
+ Vec2i columns; columns << 0, 2;
+ Mat2X extracted = ExtractColumns(A, columns);
+ EXPECT_NEAR(1, extracted(0, 0), 1e-15);
+ EXPECT_NEAR(3, extracted(0, 1), 1e-15);
+ EXPECT_NEAR(6, extracted(1, 0), 1e-15);
+ EXPECT_NEAR(8, extracted(1, 1), 1e-15);
+}
+
+TEST(Numeric, RotationRodrigues) {
+ Vec3 x, y, z;
+ x << 1, 0, 0;
+ y << 0, 1, 0;
+ z << 0, 0, 1;
+
+ Mat3 rodrigues_x = RotationRodrigues(x);
+ Mat3 rodrigues_y = RotationRodrigues(y);
+ Mat3 rodrigues_z = RotationRodrigues(z);
+
+ Mat3 Rx = RotationAroundX(1);
+ Mat3 Ry = RotationAroundY(1);
+ Mat3 Rz = RotationAroundZ(1);
+
+ EXPECT_MATRIX_NEAR(Rx, rodrigues_x, 1e-15);
+ EXPECT_MATRIX_NEAR(Ry, rodrigues_y, 1e-15);
+ EXPECT_MATRIX_NEAR(Rz, rodrigues_z, 1e-15);
+}
+
+TEST(Numeric, LookAt) {
+ // Simple orthogonality check.
+ Vec3 e; e << 1, 2, 3;
+ Mat3 R = LookAt(e), I = Mat3::Identity();
+ Mat3 RRT = R*R.transpose();
+ Mat3 RTR = R.transpose()*R;
+
+ EXPECT_MATRIX_NEAR(I, RRT, 1e-15);
+ EXPECT_MATRIX_NEAR(I, RTR, 1e-15);
+}
+
+TEST(Numeric, Reshape) {
+ Vec4 x; x << 1, 2, 3, 4;
+ Mat2 M, M_expected;
+ reshape(x, 2, 2, &M);
+ M_expected << 1, 2,
+ 3, 4;
+ EXPECT_MATRIX_NEAR(M_expected, M, 1e-15);
+}
+
+} // namespace
diff --git a/extern/libmv/libmv/numeric/poly_test.cc b/extern/libmv/libmv/numeric/poly_test.cc
new file mode 100644
index 00000000000..ea50383190f
--- /dev/null
+++ b/extern/libmv/libmv/numeric/poly_test.cc
@@ -0,0 +1,98 @@
+// Copyright (c) 2007, 2008 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/numeric/numeric.h"
+#include "libmv/numeric/poly.h"
+#include "testing/testing.h"
+
+using namespace libmv;
+
+namespace {
+
+// Find the polynomial coefficients of x in the equation
+//
+// (x - a)(x - b)(x - c) == 0
+//
+// by expanding to
+//
+// x^3 - (c+b+a) * x^2 + (a*b+(b+a)*c) * x - a*b*c = 0.
+// = p = q = r
+void CoeffsForCubicZeros(double a, double b, double c,
+ double *p, double *q, double *r) {
+ *p = -(c + b + a);
+ *q = (a * b + (b + a) * c);
+ *r = -a * b * c;
+}
+// Find the polynomial coefficients of x in the equation
+//
+// (x - a)(x - b)(x - c)(x - d) == 0
+//
+// by expanding to
+//
+// x^4 - (d+c+b+a) * x^3 + (d*(c+b+a) + a*b+(b+a)*c) * x^2
+// - (d*(a*b+(b+a)*c)+a*b*c) * x + a*b*c*d = 0.
+void CoeffsForQuarticZeros(double a, double b, double c, double d,
+ double *p, double *q, double *r, double *s) {
+ *p = -(d + c + b + a);
+ *q = (d * (c + b + a) + a * b + (b + a) * c);
+ *r = -(d * (a * b + (b + a) * c) + a * b * c);
+ *s = a * b * c *d;
+}
+
+TEST(Poly, SolveCubicPolynomial) {
+ double a, b, c, aa, bb, cc;
+ double p, q, r;
+
+ a = 1; b = 2; c = 3;
+ CoeffsForCubicZeros(a, b, c, &p, &q, &r);
+ ASSERT_EQ(3, SolveCubicPolynomial(p, q, r, &aa, &bb, &cc));
+ EXPECT_NEAR(a, aa, 1e-10);
+ EXPECT_NEAR(b, bb, 1e-10);
+ EXPECT_NEAR(c, cc, 1e-10);
+
+ a = 0; b = 1; c = 3;
+ CoeffsForCubicZeros(a, b, c, &p, &q, &r);
+ ASSERT_EQ(3, SolveCubicPolynomial(p, q, r, &aa, &bb, &cc));
+ EXPECT_NEAR(a, aa, 1e-10);
+ EXPECT_NEAR(b, bb, 1e-10);
+ EXPECT_NEAR(c, cc, 1e-10);
+
+ a = -10; b = 0; c = 1;
+ CoeffsForCubicZeros(a, b, c, &p, &q, &r);
+ ASSERT_EQ(3, SolveCubicPolynomial(p, q, r, &aa, &bb, &cc));
+ EXPECT_NEAR(a, aa, 1e-10);
+ EXPECT_NEAR(b, bb, 1e-10);
+ EXPECT_NEAR(c, cc, 1e-10);
+
+ a = -8; b = 1; c = 3;
+ CoeffsForCubicZeros(a, b, c, &p, &q, &r);
+ ASSERT_EQ(3, SolveCubicPolynomial(p, q, r, &aa, &bb, &cc));
+ EXPECT_NEAR(a, aa, 1e-10);
+ EXPECT_NEAR(b, bb, 1e-10);
+ EXPECT_NEAR(c, cc, 1e-10);
+
+ a = 28; b = 28; c = 105;
+ CoeffsForCubicZeros(a, b, c, &p, &q, &r);
+ ASSERT_EQ(3, SolveCubicPolynomial(p, q, r, &aa, &bb, &cc));
+ EXPECT_NEAR(a, aa, 1e-10);
+ EXPECT_NEAR(b, bb, 1e-10);
+ EXPECT_NEAR(c, cc, 1e-10);
+}
+} // namespace
diff --git a/extern/libmv/libmv/simple_pipeline/camera_intrinsics_test.cc b/extern/libmv/libmv/simple_pipeline/camera_intrinsics_test.cc
new file mode 100644
index 00000000000..96d35a29ef8
--- /dev/null
+++ b/extern/libmv/libmv/simple_pipeline/camera_intrinsics_test.cc
@@ -0,0 +1,239 @@
+// Copyright (c) 2011 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/simple_pipeline/camera_intrinsics.h"
+
+#include <iostream>
+
+#include "testing/testing.h"
+#include "libmv/image/image.h"
+#include "libmv/image/image_drawing.h"
+#include "libmv/logging/logging.h"
+
+namespace libmv {
+
+TEST(PolynomialCameraIntrinsics2, ApplyOnFocalCenter) {
+ PolynomialCameraIntrinsics intrinsics;
+ intrinsics.SetFocalLength(1300.0, 1300.0);
+ intrinsics.SetPrincipalPoint(640.0, 540.0);
+ intrinsics.SetRadialDistortion(-0.2, -0.1, -0.05);
+
+ double distorted_x, distorted_y;
+ intrinsics.ApplyIntrinsics(0.0, 0.0, &distorted_x, &distorted_y);
+
+ EXPECT_NEAR(640.0, distorted_x, 1e-8);
+ EXPECT_NEAR(540.0, distorted_y, 1e-8);
+}
+
+TEST(PolynomialCameraIntrinsics, InvertOnFocalCenter) {
+ PolynomialCameraIntrinsics intrinsics;
+ intrinsics.SetFocalLength(1300.0, 1300.0);
+ intrinsics.SetPrincipalPoint(640.0, 540.0);
+ intrinsics.SetRadialDistortion(-0.2, -0.1, -0.05);
+
+ double normalized_x, normalized_y;
+ intrinsics.InvertIntrinsics(640.0, 540.0, &normalized_x, &normalized_y);
+
+ EXPECT_NEAR(0.0, normalized_x, 1e-8);
+ EXPECT_NEAR(0.0, normalized_y, 1e-8);
+}
+
+TEST(PolynomialCameraIntrinsics, ApplyIntrinsics) {
+ const int N = 5;
+
+ double expected[N][N][2] = {
+ { {75.312500, -24.687500}, {338.982239, -62.035522},
+ {640.000000, -72.929688}, {941.017761, -62.035522},
+ {1204.687500, -24.687500}},
+
+ { {37.964478, 238.982239}, {323.664551, 223.664551},
+ {640.000000, 219.193420}, {956.335449, 223.664551},
+ {1242.035522, 238.982239}},
+
+ { {27.070312, 540.000000}, {319.193420, 540.000000},
+ {640.000000, 540.000000}, {960.806580, 540.000000},
+ {1252.929688, 540.000000}},
+
+ { {37.964478, 841.017761}, {323.664551, 856.335449},
+ {640.000000, 860.806580}, {956.335449, 856.335449},
+ {1242.035522, 841.017761}},
+
+ { {75.312500, 1104.687500}, {338.982239, 1142.035522},
+ {640.000000, 1152.929688}, {941.017761, 1142.035522},
+ {1204.687500, 1104.687500}}
+ };
+
+ PolynomialCameraIntrinsics intrinsics;
+ intrinsics.SetFocalLength(1300.0, 1300.0);
+ intrinsics.SetPrincipalPoint(640.0, 540.0);
+ intrinsics.SetRadialDistortion(-0.2, -0.1, -0.05);
+
+ double step = 1.0 / (N - 1);
+
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ double normalized_x = j * step - 0.5,
+ normalized_y = i * step - 0.5;
+
+ double distorted_x, distorted_y;
+ intrinsics.ApplyIntrinsics(normalized_x, normalized_y,
+ &distorted_x, &distorted_y);
+
+ EXPECT_NEAR(expected[i][j][0], distorted_x, 1e-6);
+ EXPECT_NEAR(expected[i][j][1], distorted_y, 1e-6);
+ }
+ }
+}
+
+TEST(PolynomialCameraIntrinsics, InvertIntrinsics) {
+ const int N = 5;
+
+ double expected[N][N][2] = {
+ { {-0.524482, -0.437069}, {-0.226237, -0.403994},
+ { 0.031876, -0.398446}, { 0.293917, -0.408218},
+ { 0.632438, -0.465028}},
+
+ { {-0.493496, -0.189173}, {-0.219052, -0.179936},
+ { 0.030975, -0.178107}, { 0.283742, -0.181280},
+ { 0.574557, -0.194335}},
+
+ { {-0.488013, 0.032534}, {-0.217537, 0.031077},
+ { 0.030781, 0.030781}, { 0.281635, 0.031293},
+ { 0.566344, 0.033314}},
+
+ { {-0.498696, 0.257660}, {-0.220424, 0.244041},
+ { 0.031150, 0.241409}, { 0.285660, 0.245985},
+ { 0.582670, 0.265629}},
+
+ { {-0.550617, 0.532263}, {-0.230399, 0.477255},
+ { 0.032380, 0.469510}, { 0.299986, 0.483311},
+ { 0.684740, 0.584043}}
+ };
+
+ PolynomialCameraIntrinsics intrinsics;
+ intrinsics.SetFocalLength(1300.0, 1300.0);
+ intrinsics.SetPrincipalPoint(600.0, 500.0);
+ intrinsics.SetRadialDistortion(-0.2, -0.1, -0.05);
+
+ double step_x = 1280.0 / (N - 1),
+ step_y = 1080.0 / (N - 1);
+
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ double distorted_x = j * step_x,
+ distorted_y = i * step_y;
+
+ double normalized_x, normalized_y;
+ intrinsics.InvertIntrinsics(distorted_x, distorted_y,
+ &normalized_x, &normalized_y);
+
+ EXPECT_NEAR(expected[i][j][0], normalized_x, 1e-6);
+ EXPECT_NEAR(expected[i][j][1], normalized_y, 1e-6);
+ }
+ }
+}
+
+TEST(PolynomialCameraIntrinsics, ApplyIsInvertibleSimple) {
+ PolynomialCameraIntrinsics intrinsics;
+ intrinsics.SetFocalLength(1300.0, 1300.0);
+ intrinsics.SetPrincipalPoint(640.0, 540.0);
+ intrinsics.SetRadialDistortion(-0.2, -0.1, -0.05);
+
+ // Scan over image coordinates, invert the intrinsics, then re-apply them to
+ // make sure the cycle gets back where it started.
+ for (double y = 0; y < 1000; y += 100) {
+ for (double x = 0; x < 1000; x += 100) {
+ double normalized_x, normalized_y;
+ intrinsics.InvertIntrinsics(x, y, &normalized_x, &normalized_y);
+
+ double xp, yp;
+ intrinsics.ApplyIntrinsics(normalized_x, normalized_y, &xp, &yp);
+
+ EXPECT_NEAR(x, xp, 1e-8) << "y: " << y;
+ EXPECT_NEAR(y, yp, 1e-8) << "x: " << x;
+ LG << "Error x: " << (x - xp);
+ LG << "Error y: " << (y - yp);
+ }
+ }
+}
+
+TEST(PolynomialCameraIntrinsics, IdentityDistortBuffer) {
+ const int w = 101, h = 101;
+ FloatImage image(h, w);
+ image.Fill(0);
+
+ DrawLine(0.0, h / 2.0, w - 1, h / 2.0, 1.0, &image);
+ DrawLine(0.0, h / 4.0, w - 1, h / 4.0, 1.0, &image);
+ DrawLine(0.0, h / 4.0 * 3.0, w - 1.0, h / 4.0 * 3.0, 1.0, &image);
+ DrawLine(w / 2.0, 0.0, w / 2.0, h - 1.0, 1.0, &image);
+ DrawLine(w / 4.0, 0.0, w / 4.0, h - 1.0, 1.0, &image);
+ DrawLine(w / 4.0 * 3.0, 0.0, w / 4.0 * 3.0, h - 1.0, 1.0, &image);
+
+ PolynomialCameraIntrinsics intrinsics;
+ FloatImage distorted_image(h, w);
+ intrinsics.SetImageSize(w, h);
+ intrinsics.SetFocalLength(10.0, 10.0);
+ intrinsics.SetPrincipalPoint((double) w / 2.0, (double) h / 2.0);
+ intrinsics.SetRadialDistortion(0.0, 0.0, 0.0);
+ intrinsics.DistortBuffer(image.Data(),
+ image.Width(), image.Height(),
+ 0.0,
+ image.Depth(),
+ distorted_image.Data());
+
+ for (int x = 0; x < image.Width(); ++x) {
+ for (int y = 0; y < image.Height(); ++y) {
+ EXPECT_EQ(image(y, x), distorted_image(y, x));
+ }
+ }
+}
+
+TEST(PolynomialCameraIntrinsics, IdentityUndistortBuffer) {
+ const int w = 101, h = 101;
+ FloatImage image(h, w);
+ image.Fill(0);
+
+ DrawLine(0.0, h / 2.0, w - 1, h / 2.0, 1.0, &image);
+ DrawLine(0.0, h / 4.0, w - 1, h / 4.0, 1.0, &image);
+ DrawLine(0.0, h / 4.0 * 3.0, w - 1.0, h / 4.0 * 3.0, 1.0, &image);
+ DrawLine(w / 2.0, 0.0, w / 2.0, h - 1.0, 1.0, &image);
+ DrawLine(w / 4.0, 0.0, w / 4.0, h - 1.0, 1.0, &image);
+ DrawLine(w / 4.0 * 3.0, 0.0, w / 4.0 * 3.0, h - 1.0, 1.0, &image);
+
+ PolynomialCameraIntrinsics intrinsics;
+ FloatImage distorted_image(h, w);
+ intrinsics.SetImageSize(w, h);
+ intrinsics.SetFocalLength(10.0, 10.0);
+ intrinsics.SetPrincipalPoint((double) w / 2.0, (double) h / 2.0);
+ intrinsics.SetRadialDistortion(0.0, 0.0, 0.0);
+ intrinsics.UndistortBuffer(image.Data(),
+ image.Width(), image.Height(),
+ 0.0,
+ image.Depth(),
+ distorted_image.Data());
+
+ for (int x = 0; x < image.Width(); ++x) {
+ for (int y = 0; y < image.Height(); ++y) {
+ EXPECT_EQ(image(y, x), distorted_image(y, x));
+ }
+ }
+}
+
+} // namespace libmv
diff --git a/extern/libmv/libmv/simple_pipeline/detect_test.cc b/extern/libmv/libmv/simple_pipeline/detect_test.cc
new file mode 100644
index 00000000000..fe57e3d04a2
--- /dev/null
+++ b/extern/libmv/libmv/simple_pipeline/detect_test.cc
@@ -0,0 +1,230 @@
+// Copyright (c) 2014 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/simple_pipeline/detect.h"
+
+#include "testing/testing.h"
+#include "libmv/logging/logging.h"
+
+namespace libmv {
+
+namespace {
+
+void PreformSinglePointTest(const DetectOptions &options) {
+ // Prepare the image.
+ FloatImage image(15, 15);
+ image.fill(1.0);
+ image(7, 7) = 0.0;
+
+ // Run the detector.
+ vector<Feature> detected_features;
+ Detect(image, options, &detected_features);
+
+ // Check detected features matches our expectations.
+ EXPECT_EQ(1, detected_features.size());
+ if (detected_features.size() == 1) {
+ Feature &feature = detected_features[0];
+ EXPECT_EQ(7, feature.x);
+ EXPECT_EQ(7, feature.y);
+ }
+}
+
+void PreformCheckerBoardTest(const DetectOptions &options) {
+ // Prepare the image.
+ FloatImage image(30, 30);
+ for (int y = 0; y < image.Height(); ++y) {
+ for (int x = 0; x < image.Width(); ++x) {
+ image(y, x) = (x / 10 + y / 10) % 2 ? 1.0 : 0.0;
+ }
+ }
+
+ // Run the detector.
+ vector<Feature> detected_features;
+ Detect(image, options, &detected_features);
+
+ // Check detected features matches our expectations.
+
+ // We expect here only corners of a center square to be
+ // considered a feature points.
+ EXPECT_EQ(4, detected_features.size());
+
+ // We don't know which side of the corner detector will choose,
+ // so what we're checking here is that detected feature is from
+ // any side of the corner.
+ //
+ // This doesn't check whether there're multiple features which
+ // are placed on different sides of the same corner. The way we
+ // deal with this is requiring min_distance to be greater than 2px.
+ for (int i = 0; i < detected_features.size(); ++i) {
+ Feature &feature = detected_features[i];
+ int rounded_x = ((feature.x + 1) / 10) * 10,
+ rounded_y = ((feature.y + 1) / 10) * 10;
+ EXPECT_LE(1, std::abs(feature.x - rounded_x));
+ EXPECT_LE(1, std::abs(feature.y - rounded_y));
+ }
+}
+
+void CheckExpectedFeatures(const vector<Feature> &detected_features,
+ const vector<Feature> &expected_features) {
+ EXPECT_EQ(expected_features.size(), detected_features.size());
+
+ // That's unsafe to iterate over vectors when their lengths
+ // doesn't match. And it doesn't make any sense actually since
+ // the test will already be considered failed here.
+ if (expected_features.size() != detected_features.size()) {
+ return;
+ }
+
+ for (int i = 0; i < expected_features.size(); ++i) {
+ const Feature &extected_feature = expected_features[i];
+ bool found = false;
+ for (int j = 0; j < detected_features.size(); ++j) {
+ const Feature &detected_feature = detected_features[j];
+ if (extected_feature.x == detected_feature.x &&
+ extected_feature.y == detected_feature.y) {
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found);
+ }
+}
+
+void PreformSingleTriangleTest(const DetectOptions &options) {
+ // Prepare the image.
+ FloatImage image(15, 21);
+ image.fill(1.0);
+
+ int vertex_x = 10, vertex_y = 5;
+ for (int i = 0; i < 6; ++i) {
+ int current_x = vertex_x - i,
+ current_y = vertex_y + i;
+ for (int j = 0; j < i * 2 + 1; ++j, ++current_x) {
+ image(current_y, current_x) = 0.0;
+ }
+ }
+
+ // Run the detector.
+ vector<Feature> detected_features;
+ Detect(image, options, &detected_features);
+
+ // Check detected features matches our expectations.
+ vector<Feature> expected_features;
+ expected_features.push_back(Feature(6, 10));
+ expected_features.push_back(Feature(14, 10));
+ expected_features.push_back(Feature(10, 6));
+
+ CheckExpectedFeatures(detected_features, expected_features);
+}
+
+} // namespace
+
+#ifndef LIBMV_NO_FAST_DETECTOR
+TEST(Detect, FASTSinglePointTest) {
+ DetectOptions options;
+ options.type = DetectOptions::FAST;
+ options.min_distance = 0;
+ options.fast_min_trackness = 1;
+
+ PreformSinglePointTest(options);
+}
+#endif // LIBMV_NO_FAST_DETECTOR
+
+#if 0
+// TODO(sergey): FAST doesn't detect checker board corners, but should it?
+TEST(Detect, FASTCheckerBoardTest) {
+ DetectOptions options;
+ options.type = DetectOptions::FAST;
+ options.min_distance = 0;
+ options.fast_min_trackness = 1;
+
+ PreformCheckerBoardTest(options);
+}
+#endif
+
+#if 0
+// TODO(sergey): FAST doesn't detect triangle corners!
+TEST(Detect, FASTSingleTriangleTest) {
+ DetectOptions options;
+ options.type = DetectOptions::FAST;
+ options.margin = 3;
+ options.min_distance = 0;
+ options.fast_min_trackness = 2;
+
+ PreformSingleTriangleTest(options);
+}
+#endif
+
+#if 0
+// TODO(sergey): This doesn't actually detect single point,
+// but should it or it's expected that Moravec wouldn't consider
+// single point as feature?
+//
+// Uncomment this or remove as soon as we know answer for the
+// question.
+TEST(Detect, MoravecSinglePointTest) {
+ DetectOptions options;
+ options.type = DetectOptions::MORAVEC;
+ options.min_distance = 0;
+ options.moravec_max_count = 10;
+
+ PreformSinglePointTest(options);
+}
+
+// TODO(sergey): Moravec doesn't detect checker board corners, but should it?
+TEST(Detect, MoravecCheckerBoardTest) {
+ DetectOptions options;
+ options.type = DetectOptions::MORAVEC;
+ options.min_distance = 0;
+ options.moravec_max_count = 10;
+
+ PreformCheckerBoardTest(options);
+}
+#endif
+
+TEST(Detect, HarrisSinglePointTest) {
+ DetectOptions options;
+ options.type = DetectOptions::HARRIS;
+
+ // Set this to non-zero so image corners are not considered
+ // a feature points and avoid center point neighbors to be
+ // considered a features as well.
+ options.margin = 3;
+ options.min_distance = 3;
+
+ PreformSinglePointTest(options);
+}
+
+TEST(Detect, HarrisSingleTriangleTest) {
+ DetectOptions options;
+ options.type = DetectOptions::HARRIS;
+
+ options.margin = 3;
+ options.min_distance = 2;
+ options.harris_threshold = 1e-3;
+
+ PreformSingleTriangleTest(options);
+}
+
+// TODO(sergey): Add tests for margin option.
+
+// TODO(sergey): Add tests for min_distance option.
+
+} // namespace libmv
diff --git a/extern/libmv/libmv/simple_pipeline/intersect_test.cc b/extern/libmv/libmv/simple_pipeline/intersect_test.cc
new file mode 100644
index 00000000000..dd4fdc789af
--- /dev/null
+++ b/extern/libmv/libmv/simple_pipeline/intersect_test.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2007, 2008 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/simple_pipeline/intersect.h"
+
+#include <iostream>
+
+#include "testing/testing.h"
+#include "libmv/multiview/projection.h"
+#include "libmv/numeric/numeric.h"
+#include "libmv/logging/logging.h"
+
+namespace libmv {
+
+TEST(Intersect, EuclideanIntersect) {
+ Mat3 K1 = Mat3::Identity();
+ // K1 << 320, 0, 160,
+ // 0, 320, 120,
+ // 0, 0, 1;
+ Mat3 K2 = Mat3::Identity();
+ // K2 << 360, 0, 170,
+ // 0, 360, 110,
+ // 0, 0, 1;
+ Mat3 R1 = RotationAroundZ(-0.1);
+ Mat3 R2 = RotationAroundX(-0.1);
+ Vec3 t1; t1 << 1, 1, 10;
+ Vec3 t2; t2 << -2, -1, 10;
+ Mat34 P1, P2;
+ P_From_KRt(K1, R1, t1, &P1);
+ P_From_KRt(K2, R2, t2, &P2);
+
+ //Mat3 F; FundamentalFromProjections(P1, P2, &F);
+
+ Mat3X X;
+ X.resize(3, 30);
+ X.setRandom();
+
+ Mat2X X1, X2;
+ Project(P1, X, &X1);
+ Project(P2, X, &X2);
+
+ for (int i = 0; i < X.cols(); ++i) {
+ Vec2 x1, x2;
+ MatrixColumn(X1, i, &x1);
+ MatrixColumn(X2, i, &x2);
+ Vec3 expected;
+ MatrixColumn(X, i, &expected);
+
+ EuclideanReconstruction reconstruction;
+ reconstruction.InsertCamera(1, R1, t1);
+ reconstruction.InsertCamera(2, R2, t2);
+
+ vector<Marker> markers;
+ Marker a = { 1, 0, x1.x(), x1.y(), 1.0 };
+ markers.push_back(a);
+ Marker b = { 2, 0, x2.x(), x2.y(), 1.0 };
+ markers.push_back(b);
+
+ EuclideanIntersect(markers, &reconstruction);
+ Vec3 estimated = reconstruction.PointForTrack(0)->X;
+ EXPECT_NEAR(0, DistanceLInfinity(estimated, expected), 1e-8);
+ }
+}
+} // namespace
diff --git a/extern/libmv/libmv/simple_pipeline/keyframe_selection_test.cc b/extern/libmv/libmv/simple_pipeline/keyframe_selection_test.cc
new file mode 100644
index 00000000000..9d88362cc88
--- /dev/null
+++ b/extern/libmv/libmv/simple_pipeline/keyframe_selection_test.cc
@@ -0,0 +1,307 @@
+// Copyright (c) 2011 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/simple_pipeline/keyframe_selection.h"
+
+#include "testing/testing.h"
+#include "libmv/simple_pipeline/camera_intrinsics.h"
+#include "libmv/logging/logging.h"
+
+namespace libmv {
+
+// Synthetic test, N markers with the same translation
+// Should not be keyframe
+TEST(KeyframeSelection, SyntheticNeighborFrame) {
+ PolynomialCameraIntrinsics intrinsics;
+ intrinsics.SetFocalLength(900.0,900.0);
+ intrinsics.SetPrincipalPoint(640.0, 540.0);
+ intrinsics.SetRadialDistortion(0.0, 0.0, 0.0);
+
+ Tracks tracks;
+ const int markers_per_size = 15;
+
+ // Fill in tracks for homography estimation
+ for (int x = 0; x < markers_per_size; x++) {
+ for (int y = 0; y < markers_per_size; y++) {
+ double current_x = 10 + x * 40, current_y = 10 + y * 40;
+ double next_x = current_x + 10, next_y = current_y + 10;
+
+ intrinsics.InvertIntrinsics(current_x, current_y, &current_x, &current_y);
+ intrinsics.InvertIntrinsics(next_x, next_y, &next_x, &next_y);
+
+ tracks.Insert(1, y * markers_per_size + x, current_x, current_y);
+ tracks.Insert(2, y * markers_per_size + x, next_x, next_y);
+ }
+ }
+
+ vector<int> keyframes;
+ SelectKeyframesBasedOnGRICAndVariance(tracks, intrinsics, keyframes);
+
+ // Synthetic second frame shouldn't be considered a keyframe
+ EXPECT_EQ(0, keyframes.size());
+}
+
+// Frames 1 and 2 of FabrikEingang footage
+// Only one wall is tracked, should not be keyframes
+TEST(KeyframeSelection, FabrikEingangNeighborFrames) {
+ PolynomialCameraIntrinsics intrinsics;
+ intrinsics.SetFocalLength(1605.797, 1605.797);
+ intrinsics.SetPrincipalPoint(960.000, 544.000);
+ intrinsics.SetRadialDistortion(0.0, 0.0, 0.0);
+
+ Marker markers[] = {
+ {1, 0, 737.599983, 646.397594, 1.0}, {2, 0, 737.906628, 648.113327, 1.0}, {1, 1, 863.045425, 646.081905, 1.0},
+ {2, 1, 863.339767, 647.650040, 1.0}, {1, 2, 736.959972, 574.080151, 1.0}, {2, 2, 737.217350, 575.604900, 1.0},
+ {1, 3, 864.097424, 573.374908, 1.0}, {2, 3, 864.383469, 574.900307, 1.0}, {1, 4, 789.429073, 631.677521, 1.0},
+ {2, 4, 789.893131, 633.124451, 1.0}, {1, 5, 791.051960, 573.442028, 1.0}, {2, 5, 791.336575, 575.088890, 1.0},
+ {1, 6, 738.973961, 485.130308, 1.0}, {2, 6, 739.435501, 486.734207, 1.0}, {1, 7, 862.403240, 514.866074, 1.0},
+ {2, 7, 862.660618, 516.413261, 1.0}, {1, 8, 802.240162, 485.759838, 1.0}, {2, 8, 802.602253, 487.432899, 1.0},
+ {1, 9, 754.340630, 500.624559, 1.0}, {2, 9, 754.559956, 502.079920, 1.0}, {1, 10, 849.398689, 484.480545, 1.0},
+ {2, 10, 849.599934, 486.079937, 1.0}, {1, 11, 788.803768, 515.924391, 1.0}, {2, 11, 789.119911, 517.439932, 1.0},
+ {1, 12, 838.733940, 558.212688, 1.0}, {2, 12, 839.039898, 559.679916, 1.0}, {1, 13, 760.014782, 575.194466, 1.0},
+ {2, 13, 760.319881, 576.639904, 1.0}, {1, 14, 765.321636, 616.015957, 1.0}, {2, 14, 765.759945, 617.599915, 1.0},
+ {1, 15, 800.963230, 660.032082, 1.0}, {2, 15, 801.279945, 661.759876, 1.0}, {1, 16, 846.321087, 602.313053, 1.0},
+ {2, 16, 846.719913, 603.839878, 1.0}, {1, 17, 864.288311, 616.790524, 1.0}, {2, 17, 864.639931, 618.239918, 1.0},
+ {1, 18, 800.006790, 602.573425, 1.0}, {2, 18, 800.319958, 604.159912, 1.0}, {1, 19, 739.026890, 617.944138, 1.0},
+ {2, 19, 739.199924, 619.519924, 1.0}, {1, 20, 801.987419, 544.134888, 1.0}, {2, 20, 802.239933, 545.599911, 1.0},
+ {1, 21, 753.619823, 542.961300, 1.0}, {2, 21, 753.919945, 544.639874, 1.0}, {1, 22, 787.921257, 499.910206, 1.0},
+ {2, 22, 788.159924, 501.439917, 1.0}, {1, 23, 839.095459, 529.287903, 1.0}, {2, 23, 839.359932, 530.879934, 1.0},
+ {1, 24, 811.760330, 630.732269, 1.0}, {2, 24, 812.159901, 632.319859, 1.0}
+ };
+ int num_markers = sizeof(markers) / sizeof(Marker);
+
+ Tracks tracks;
+ for (int i = 0; i < num_markers; i++) {
+ double x = markers[i].x, y = markers[i].y;
+ intrinsics.InvertIntrinsics(x, y, &x, &y);
+ tracks.Insert(markers[i].image, markers[i].track, x, y);
+ }
+
+ vector<int> keyframes;
+ SelectKeyframesBasedOnGRICAndVariance(tracks, intrinsics, keyframes);
+
+ EXPECT_EQ(0, keyframes.size());
+}
+
+// Frames 120 and 200 from FabrikEingang footage
+// Should be enough of parallax for keyframing
+TEST(KeyframeSelection, FabrikEingangFarFrames) {
+ PolynomialCameraIntrinsics intrinsics;
+ intrinsics.SetFocalLength(1605.797, 1605.797);
+ intrinsics.SetPrincipalPoint(960.000, 544.000);
+ intrinsics.SetRadialDistortion(0.0, 0.0, 0.0);
+
+ Marker markers[] = {
+ {1, 0, 369.459200, 619.315258, 1.0}, {2, 0, 279.677496, 722.086842, 1.0}, {1, 1, 376.831970, 370.278397, 1.0},
+ {2, 1, 221.695247, 460.065418, 1.0}, {1, 2, 1209.139023, 567.705605, 1.0}, {2, 2, 1080.760117, 659.230083, 1.0},
+ {1, 3, 1643.495750, 903.620453, 1.0}, {2, 3, 1618.405037, 1015.374908, 1.0}, {1, 4, 1494.849815, 425.302460, 1.0},
+ {2, 4, 1457.467575, 514.727587, 1.0}, {1, 5, 1794.637299, 328.728609, 1.0}, {2, 5, 1742.161446, 408.988636, 1.0},
+ {1, 6, 1672.822723, 102.240358, 1.0}, {2, 6, 1539.287224, 153.536892, 1.0}, {1, 7, 1550.843925, 53.424943, 1.0},
+ {2, 7, 1385.579109, 96.450085, 1.0}, {1, 8, 852.953281, 465.399578, 1.0}, {2, 8, 779.404564, 560.091843, 1.0},
+ {1, 9, 906.853752, 299.827040, 1.0}, {2, 9, 786.923218, 385.570770, 1.0}, {1, 10, 406.322966, 87.556041, 1.0},
+ {2, 10, 140.339413, 150.877481, 1.0}, {1, 11, 254.811573, 851.296478, 1.0}, {2, 11, 94.478302, 969.350189, 1.0},
+ {1, 12, 729.087868, 806.092758, 1.0}, {2, 12, 606.212139, 919.876560, 1.0}, {1, 13, 1525.719452, 920.398083, 1.0},
+ {2, 13, 1495.579720, 1031.971218, 1.0}
+ };
+ int num_markers = sizeof(markers) / sizeof(Marker);
+
+ Tracks tracks;
+ for (int i = 0; i < num_markers; i++) {
+ double x = markers[i].x, y = markers[i].y;
+ intrinsics.InvertIntrinsics(x, y, &x, &y);
+ tracks.Insert(markers[i].image, markers[i].track, x, y);
+ }
+
+ vector<int> keyframes;
+ SelectKeyframesBasedOnGRICAndVariance(tracks, intrinsics, keyframes);
+
+ EXPECT_EQ(2, keyframes.size());
+}
+
+// Manually selected keyframes from copter footage from Sebastian
+// Keyframes were 167 and 237
+TEST(KeyframeSelection, CopterManualKeyFrames) {
+ PolynomialCameraIntrinsics intrinsics;
+ intrinsics.SetFocalLength(1155.043, 1155.043);
+ intrinsics.SetPrincipalPoint(640.000, 360.000);
+ intrinsics.SetRadialDistortion(-0.08590, 0.0, 0.0);
+
+ Marker markers[] = {
+ {1, 0, 645.792694, 403.115931, 1.0}, {2, 0, 630.641174, 307.996409, 1.0}, {1, 1, 783.469086, 403.904328, 1.0},
+ {2, 1, 766.001129, 308.998225, 1.0}, {1, 2, 650.000000, 160.000001, 1.0}, {1, 3, 785.225906, 158.619039, 1.0},
+ {2, 3, 767.526474, 70.449695, 1.0}, {1, 4, 290.640526, 382.335634, 1.0}, {2, 4, 273.001728, 86.993319, 1.0},
+ {1, 5, 291.162739, 410.602684, 1.0}, {2, 5, 273.287849, 111.937487, 1.0}, {1, 6, 136.919317, 349.895797, 1.0},
+ {1, 7, 490.844345, 47.572222, 1.0}, {1, 8, 454.406433, 488.935761, 1.0}, {1, 9, 378.655815, 618.522248, 1.0},
+ {2, 9, 357.061806, 372.265077, 1.0}, {1, 10, 496.011391, 372.668824, 1.0}, {2, 10, 477.979164, 222.986112, 1.0},
+ {1, 11, 680.060272, 256.103625, 1.0}, {2, 11, 670.587540, 204.830453, 1.0}, {1, 12, 1070.817108, 218.775322, 1.0},
+ {2, 12, 1046.129913, 128.969783, 1.0}, {1, 14, 242.516403, 596.048512, 1.0}, {2, 14, 224.182606, 248.272154, 1.0},
+ {1, 15, 613.936272, 287.519073, 1.0}, {2, 15, 600.467644, 196.085722, 1.0}, {1, 31, 844.637451, 256.354315, 1.0},
+ {2, 31, 823.200150, 165.714952, 1.0},
+ };
+ int num_markers = sizeof(markers) / sizeof(Marker);
+
+ Tracks tracks;
+ for (int i = 0; i < num_markers; i++) {
+ double x = markers[i].x, y = markers[i].y;
+ intrinsics.InvertIntrinsics(x, y, &x, &y);
+ tracks.Insert(markers[i].image, markers[i].track, x, y);
+ }
+
+ vector<int> keyframes;
+ SelectKeyframesBasedOnGRICAndVariance(tracks, intrinsics, keyframes);
+
+ EXPECT_EQ(2, keyframes.size());
+}
+
+// Used old friend elevator scene MMI_2366 with automatic feature selection
+// and manual outlier elimination and manual keyframe selection
+// Selected keyframes were 29 and 41
+TEST(KeyframeSelection, ElevatorManualKeyframesFrames) {
+ PolynomialCameraIntrinsics intrinsics;
+ intrinsics.SetFocalLength(1380.000, 1380.000);
+ intrinsics.SetPrincipalPoint(960.000, 540.000);
+ intrinsics.SetRadialDistortion(-0.034, 0.0, 0.0);
+
+ Marker markers[] = {
+ {1, 2, 1139.861412, 1034.634984, 1.0}, {2, 2, 1143.512192, 1065.355718, 1.0}, {1, 3, 1760.821953, 644.658036, 1.0},
+ {2, 3, 1770.901108, 697.899928, 1.0}, {1, 4, 858.071823, 1068.520746, 1.0}, {1, 6, 1633.952408, 797.050145, 1.0},
+ {2, 6, 1642.508469, 849.157140, 1.0}, {1, 8, 1716.695824, 451.805491, 1.0}, {2, 8, 1726.513939, 502.095687, 1.0},
+ {1, 9, 269.577627, 724.986935, 1.0}, {2, 9, 269.424820, 764.154246, 1.0}, {1, 10, 1891.321907, 706.948843, 1.0},
+ {2, 10, 1903.338547, 766.068377, 1.0}, {1, 12, 1806.227074, 956.089604, 1.0}, {2, 12, 1816.619568, 1013.767376, 1.0},
+ {1, 14, 269.544153, 1002.333570, 1.0}, {2, 14, 269.367542, 1043.509254, 1.0}, {1, 15, 1402.772141, 281.392962, 1.0},
+ {2, 15, 1409.089165, 318.731629, 1.0}, {1, 16, 195.877233, 919.454341, 1.0}, {2, 16, 192.531109, 997.367899, 1.0},
+ {1, 17, 1789.584045, 120.036661, 1.0}, {2, 17, 1800.391846, 167.822964, 1.0}, {1, 18, 999.363213, 765.004807, 1.0},
+ {2, 18, 1002.345772, 790.560122, 1.0}, {1, 19, 647.342491, 1044.805727, 1.0}, {2, 19, 649.328041, 1058.682940, 1.0},
+ {1, 20, 1365.486832, 440.901829, 1.0}, {2, 20, 1371.413040, 477.888730, 1.0}, {1, 21, 1787.125282, 301.431606, 1.0},
+ {2, 21, 1798.527260, 355.224531, 1.0}, {1, 22, 1257.805824, 932.797258, 1.0}, {2, 22, 1263.017578, 969.376774, 1.0},
+ {1, 23, 961.969528, 843.148112, 1.0}, {2, 23, 964.869461, 868.587620, 1.0}, {1, 24, 158.076110, 1052.643592, 1.0},
+ {1, 25, 1072.884521, 1005.296981, 1.0}, {2, 25, 1076.091156, 1032.776856, 1.0}, {1, 26, 1107.656937, 526.577228, 1.0},
+ {2, 26, 1111.618423, 555.524454, 1.0}, {1, 27, 1416.410751, 529.857581, 1.0}, {2, 27, 1422.663574, 570.025957, 1.0},
+ {1, 28, 1498.673630, 1005.453086, 1.0}, {2, 28, 1505.381813, 1051.827149, 1.0}, {1, 29, 1428.647804, 652.473629, 1.0},
+ {2, 29, 1434.898224, 692.715390, 1.0}, {1, 30, 1332.318764, 503.673599, 1.0}, {2, 30, 1338.000069, 540.507967, 1.0},
+ {1, 32, 1358.642693, 709.837904, 1.0}, {2, 32, 1364.231529, 748.678265, 1.0}, {1, 33, 1850.911560, 460.475668, 1.0},
+ {2, 33, 1862.221413, 512.797347, 1.0}, {1, 34, 1226.117821, 607.053959, 1.0}, {2, 34, 1230.736084, 641.091449, 1.0},
+ {1, 35, 619.598236, 523.341744, 1.0}, {2, 35, 621.601124, 554.453287, 1.0}, {1, 36, 956.591492, 958.223183, 1.0},
+ {2, 36, 959.289265, 983.289263, 1.0}, {1, 37, 1249.922218, 419.095856, 1.0}, {2, 37, 1255.005913, 452.556177, 1.0},
+ {1, 39, 1300.528450, 386.251166, 1.0}, {2, 39, 1305.957413, 420.185595, 1.0}, {1, 40, 1128.689919, 972.558346, 1.0},
+ {2, 40, 1132.413712, 1003.984737, 1.0}, {1, 41, 503.304749, 1053.504388, 1.0}, {2, 41, 505.019703, 1069.175613, 1.0},
+ {1, 42, 1197.352982, 472.681564, 1.0}, {2, 42, 1201.910706, 503.459880, 1.0}, {1, 43, 1794.391022, 383.911400, 1.0},
+ {2, 43, 1805.324135, 436.116468, 1.0}, {1, 44, 789.641418, 1058.045647, 1.0}, {1, 45, 1376.575241, 928.714979, 1.0},
+ {2, 45, 1381.995850, 969.511957, 1.0}, {1, 46, 1598.023567, 93.975592, 1.0}, {2, 46, 1606.937141, 136.827035, 1.0},
+ {1, 47, 1455.550232, 762.128685, 1.0}, {2, 47, 1462.014313, 805.164878, 1.0}, {1, 48, 1357.123489, 354.460326, 1.0},
+ {2, 48, 1363.071899, 390.363121, 1.0}, {1, 49, 939.792652, 781.549895, 1.0}, {2, 49, 942.802620, 806.164012, 1.0},
+ {1, 50, 1380.251083, 805.948620, 1.0}, {2, 50, 1385.637932, 845.592098, 1.0}, {1, 51, 1021.769943, 1049.802361, 1.0},
+ {1, 52, 1065.634918, 608.099055, 1.0}, {2, 52, 1069.142189, 635.361736, 1.0}, {1, 53, 624.324188, 463.202863, 1.0},
+ {2, 53, 626.395454, 494.994088, 1.0}, {1, 54, 1451.459885, 881.557624, 1.0}, {2, 54, 1457.679634, 924.345531, 1.0},
+ {1, 55, 1201.885986, 1057.079022, 1.0}, {1, 56, 581.157532, 947.661438, 1.0}, {2, 56, 583.242359, 960.831449, 1.0},
+ {1, 58, 513.593102, 954.175858, 1.0}, {2, 58, 515.470047, 971.309574, 1.0}, {1, 59, 928.069038, 901.774421, 1.0},
+ {2, 59, 930.847950, 925.613744, 1.0}, {1, 60, 1065.860023, 740.395389, 1.0}, {2, 60, 1069.484253, 768.971086, 1.0},
+ {1, 61, 990.479393, 906.264632, 1.0}, {2, 61, 993.217506, 933.088803, 1.0}, {1, 62, 1776.196747, 776.278453, 1.0},
+ {2, 62, 1786.292496, 831.136880, 1.0}, {1, 63, 834.454365, 1012.449725, 1.0}, {2, 63, 836.868324, 1033.451807, 1.0},
+ {1, 64, 1355.190697, 869.184809, 1.0}, {2, 64, 1360.736618, 909.773347, 1.0}, {1, 65, 702.072487, 897.519686, 1.0},
+ {2, 65, 704.203377, 911.931131, 1.0}, {1, 66, 1214.022903, 856.199934, 1.0}, {2, 66, 1218.109016, 890.753052, 1.0},
+ {1, 67, 327.676048, 236.814036, 1.0}, {2, 67, 328.335285, 277.251878, 1.0}, {1, 68, 289.064083, 454.793912, 1.0},
+ {2, 68, 288.651924, 498.882444, 1.0}, {1, 69, 1626.240692, 278.374350, 1.0}, {2, 69, 1634.131508, 315.853672, 1.0},
+ {1, 70, 1245.375710, 734.862142, 1.0}, {2, 70, 1250.047417, 769.670885, 1.0}, {1, 71, 497.015305, 510.718904, 1.0},
+ {2, 71, 498.682308, 541.070201, 1.0}, {1, 72, 1280.542030, 153.939185, 1.0}, {2, 72, 1286.993637, 198.436196, 1.0},
+ {1, 73, 1534.748840, 138.601043, 1.0}, {2, 73, 1542.961349, 180.170819, 1.0}, {1, 74, 1477.412682, 200.608061, 1.0},
+ {2, 74, 1484.683914, 240.413260, 1.0}, {1, 76, 450.637321, 407.279642, 1.0}, {2, 76, 451.695642, 441.666291, 1.0},
+ {1, 78, 246.981239, 220.786298, 1.0}, {2, 78, 244.524879, 290.016564, 1.0}, {1, 79, 36.696489, 420.023407, 1.0},
+ {2, 79, 21.364746, 591.245492, 1.0},
+ };
+ int num_markers = sizeof(markers) / sizeof(Marker);
+
+ Tracks tracks;
+ for (int i = 0; i < num_markers; i++) {
+ double x = markers[i].x, y = markers[i].y;
+ intrinsics.InvertIntrinsics(x, y, &x, &y);
+ tracks.Insert(markers[i].image, markers[i].track, x, y);
+ }
+
+ vector<int> keyframes;
+ SelectKeyframesBasedOnGRICAndVariance(tracks, intrinsics, keyframes);
+
+ EXPECT_EQ(2, keyframes.size());
+}
+
+// Elevator scene MMI_2366 with manual tracks, frames 1, 2, 3, 5 and 27
+TEST(KeyframeSelection, ElevatorReconstructionVarianceTest) {
+ PolynomialCameraIntrinsics intrinsics;
+ intrinsics.SetFocalLength(1380.000, 1380.000);
+ intrinsics.SetPrincipalPoint(960.000, 540.000);
+ intrinsics.SetRadialDistortion(-0.034, 0.0, 0.0);
+
+ Marker markers[] = {
+ {1, 0, 182.999997, 1047.000010, 1.0}, {2, 0, 181.475730, 1052.091079, 1.0}, {3, 0, 181.741562, 1057.893341, 1.0},
+ {4, 0, 183.190498, 1068.310440, 1.0}, {1, 1, 271.000013, 666.000009, 1.0}, {2, 1, 270.596180, 668.665760, 1.0},
+ {3, 1, 270.523510, 671.559069, 1.0}, {4, 1, 271.856518, 676.818151, 1.0}, {5, 1, 268.989000, 727.051570, 1.0},
+ {1, 2, 264.999990, 1018.000031, 1.0}, {2, 2, 264.020061, 1021.157591, 1.0}, {3, 2, 264.606056, 1024.823506, 1.0},
+ {4, 2, 266.200933, 1031.168690, 1.0}, {1, 3, 270.000000, 938.000014, 1.0}, {2, 3, 269.022617, 941.153390, 1.0},
+ {3, 3, 269.605579, 944.454954, 1.0}, {4, 3, 271.281366, 949.452167, 1.0}, {5, 3, 268.963480, 1004.417453, 1.0},
+ {1, 4, 200.999994, 799.000003, 1.0}, {2, 4, 199.841366, 803.891838, 1.0}, {3, 4, 200.262208, 809.323246, 1.0},
+ {4, 4, 201.456513, 819.271195, 1.0}, {5, 4, 195.026493, 924.363234, 1.0}, {1, 5, 1775.000038, 49.999998, 1.0},
+ {2, 5, 1775.255127, 53.718264, 1.0}, {3, 5, 1776.449890, 55.951670, 1.0}, {4, 5, 1778.815727, 61.923309, 1.0},
+ {5, 5, 1790.274124, 123.074923, 1.0}, {1, 6, 164.000001, 927.999988, 1.0}, {2, 6, 162.665462, 933.169527, 1.0},
+ {3, 6, 163.067923, 938.577182, 1.0}, {4, 6, 164.370360, 948.840945, 1.0}, {5, 6, 157.199407, 1057.762341, 1.0},
+ {1, 7, 618.000011, 477.999998, 1.0}, {2, 7, 617.583504, 480.124243, 1.0}, {3, 7, 618.356495, 482.441897, 1.0},
+ {4, 7, 619.792500, 486.428132, 1.0}, {5, 7, 619.546051, 525.222627, 1.0}, {1, 8, 499.999981, 1036.999984, 1.0},
+ {2, 8, 499.080162, 1038.720160, 1.0}, {3, 8, 499.949398, 1039.014344, 1.0}, {4, 8, 501.828003, 1041.286647, 1.0},
+ {5, 8, 502.777576, 1055.196369, 1.0}, {1, 9, 1587.000046, 31.999999, 1.0}, {2, 9, 1586.988373, 34.635853, 1.0},
+ {3, 9, 1588.155899, 37.444186, 1.0}, {4, 9, 1589.973106, 42.492081, 1.0}, {5, 9, 1598.683205, 96.526332, 1.0},
+ {1, 10, 622.999992, 416.999999, 1.0}, {2, 10, 622.449017, 419.233485, 1.0}, {3, 10, 623.283234, 421.500703, 1.0},
+ {4, 10, 624.620132, 425.537406, 1.0}, {5, 10, 624.290829, 465.078338, 1.0}, {1, 11, 577.999992, 931.999998, 1.0},
+ {2, 11, 577.042294, 932.872703, 1.0}, {3, 11, 577.832451, 934.045451, 1.0}, {4, 11, 579.729137, 935.735435, 1.0},
+ {5, 11, 580.691242, 948.396256, 1.0}, {1, 12, 510.999985, 931.999998, 1.0}, {2, 12, 510.111237, 933.152146, 1.0},
+ {3, 12, 510.797081, 934.454219, 1.0}, {4, 12, 512.647362, 936.595910, 1.0}, {5, 12, 513.247204, 955.144157, 1.0},
+ {1, 13, 330.459995, 177.059993, 1.0}, {2, 13, 329.876347, 179.615586, 1.0}, {3, 13, 330.681696, 182.757810, 1.0},
+ {4, 13, 331.345053, 187.903853, 1.0}, {5, 13, 327.824135, 239.611639, 1.0}, {1, 14, 291.813097, 388.516195, 1.0},
+ {2, 14, 290.984058, 391.382725, 1.0}, {3, 14, 291.526737, 394.778595, 1.0}, {4, 14, 292.763815, 400.310973, 1.0},
+ {5, 14, 288.714552, 457.548015, 1.0}, {1, 15, 496.491680, 466.534005, 1.0}, {2, 15, 495.909519, 468.518561, 1.0},
+ {3, 15, 496.588383, 470.853596, 1.0}, {4, 15, 497.976780, 474.731458, 1.0}, {5, 15, 496.998882, 512.568694, 1.0},
+ {1, 16, 1273.000031, 89.000000, 1.0}, {2, 16, 1272.951965, 92.003637, 1.0}, {3, 16, 1273.934784, 94.972191, 1.0},
+ {4, 16, 1275.493584, 100.139952, 1.0}, {5, 16, 1281.003571, 156.880163, 1.0}, {1, 17, 1524.713173, 78.852922, 1.0},
+ {2, 17, 1524.782066, 81.427142, 1.0}, {3, 17, 1525.759048, 84.057939, 1.0}, {4, 17, 1527.579689, 88.966550, 1.0},
+ {5, 17, 1535.262451, 141.186054, 1.0}, {1, 18, 1509.425011, 94.371824, 1.0}, {1, 19, 451.000013, 357.000003, 1.0},
+ {2, 19, 450.354881, 359.312410, 1.0}, {3, 19, 451.107473, 361.837088, 1.0}, {4, 19, 452.186537, 366.318061, 1.0},
+ {5, 19, 450.507660, 409.257599, 1.0}, {1, 20, 254.004936, 114.784185, 1.0}, {2, 20, 253.291512, 119.288486, 1.0},
+ {3, 20, 253.745584, 124.114957, 1.0}, {4, 20, 254.453287, 132.795120, 1.0}, {5, 20, 246.772242, 225.165337, 1.0},
+ {1, 21, 65.262880, 147.889409, 1.0}, {2, 21, 63.634465, 157.656807, 1.0}, {3, 21, 63.306799, 169.067053, 1.0},
+ {4, 21, 62.462311, 189.724241, 1.0}, {5, 21, 35.396615, 430.308380, 1.0},
+ };
+ int num_markers = sizeof(markers) / sizeof(Marker);
+
+ Tracks tracks;
+ for (int i = 0; i < num_markers; i++) {
+ double x = markers[i].x, y = markers[i].y;
+ intrinsics.InvertIntrinsics(x, y, &x, &y);
+ tracks.Insert(markers[i].image, markers[i].track, x, y);
+ }
+
+ vector<int> keyframes;
+ SelectKeyframesBasedOnGRICAndVariance(tracks, intrinsics, keyframes);
+
+ EXPECT_EQ(2, keyframes.size());
+ if (keyframes.size() == 2) {
+ EXPECT_EQ(1, keyframes[0]);
+ EXPECT_EQ(5, keyframes[1]);
+ }
+}
+
+} // namespace libmv
diff --git a/extern/libmv/libmv/simple_pipeline/modal_solver_test.cc b/extern/libmv/libmv/simple_pipeline/modal_solver_test.cc
new file mode 100644
index 00000000000..8b87acd95bb
--- /dev/null
+++ b/extern/libmv/libmv/simple_pipeline/modal_solver_test.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2013 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/simple_pipeline/modal_solver.h"
+
+#include "testing/testing.h"
+#include "libmv/logging/logging.h"
+#include "libmv/simple_pipeline/bundle.h"
+#include "libmv/simple_pipeline/camera_intrinsics.h"
+
+#include <stdio.h>
+
+namespace libmv {
+
+TEST(ModalSolver, SyntheticCubeSceneMotion) {
+ double kTolerance = 1e-8;
+
+ PolynomialCameraIntrinsics intrinsics;
+ intrinsics.SetFocalLength(658.286, 658.286);
+ intrinsics.SetPrincipalPoint(480.0, 270.0);
+ intrinsics.SetRadialDistortion(0.0, 0.0, 0.0);
+
+ Marker markers[] = {
+ {1, 0, 212.172775, 354.713538, 1.0}, {2, 0, 773.468399, 358.735306, 1.0},
+ {1, 1, 62.415197, 287.905354, 1.0}, {2, 1, 619.103336, 324.402537, 1.0},
+ {1, 2, 206.847939, 237.567925, 1.0}, {2, 2, 737.496986, 247.881383, 1.0},
+ {1, 3, 351.743889, 316.415906, 1.0}, {2, 3, 908.779621, 290.703617, 1.0},
+ {1, 4, 232.941413, 54.265443, 1.0}, {2, 4, 719.444847, 63.062531, 1.0},
+ {1, 5, 96.391611, 119.283537, 1.0}, {2, 5, 611.413136, 160.890715, 1.0},
+ {1, 6, 363.444958, 150.838144, 1.0}, {2, 6, 876.374531, 114.916206, 1.0},
+ };
+ int num_markers = sizeof(markers) / sizeof(Marker);
+
+ Tracks tracks;
+ for (int i = 0; i < num_markers; i++) {
+ double x = markers[i].x, y = markers[i].y;
+ intrinsics.InvertIntrinsics(x, y, &x, &y);
+ tracks.Insert(markers[i].image, markers[i].track, x, y);
+ }
+
+ EuclideanReconstruction reconstruction;
+ ModalSolver(tracks, &reconstruction);
+ EuclideanBundleCommonIntrinsics(tracks,
+ BUNDLE_NO_INTRINSICS,
+ BUNDLE_NO_TRANSLATION,
+ &reconstruction,
+ &intrinsics,
+ NULL);
+
+ Mat3 expected_rotation;
+ expected_rotation << 0.98215101299251, 0.17798357184544, 0.06083778292258,
+ -0.16875286001759, 0.97665299913606, -0.13293378620359,
+ -0.08307743323957, 0.12029450291547, 0.98925596922871;
+
+ Mat3 &first_camera_R = reconstruction.CameraForImage(1)->R;
+ Mat3 &second_camera_R = reconstruction.CameraForImage(2)->R;
+
+ EXPECT_TRUE(Mat3::Identity().isApprox(first_camera_R, kTolerance));
+ EXPECT_TRUE(expected_rotation.isApprox(second_camera_R, kTolerance));
+}
+
+} // namespace libmv
diff --git a/extern/libmv/libmv/simple_pipeline/resect_test.cc b/extern/libmv/libmv/simple_pipeline/resect_test.cc
new file mode 100644
index 00000000000..811edd282d8
--- /dev/null
+++ b/extern/libmv/libmv/simple_pipeline/resect_test.cc
@@ -0,0 +1,234 @@
+// Copyright (c) 2009 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/simple_pipeline/resect.h"
+#include "libmv/logging/logging.h"
+#include "testing/testing.h"
+
+#if 0
+// Generates all necessary inputs and expected outputs for EuclideanResection.
+void CreateCameraSystem(const Mat3& KK,
+ const Mat3X& x_image,
+ const Vec& X_distances,
+ const Mat3& R_input,
+ const Vec3& T_input,
+ Mat2X *x_camera,
+ Mat3X *X_world,
+ Mat3 *R_expected,
+ Vec3 *T_expected) {
+ int num_points = x_image.cols();
+
+ Mat3X x_unit_cam(3, num_points);
+ x_unit_cam = KK.inverse() * x_image;
+
+ // Create normalized camera coordinates to be used as an input to the PnP
+ // function, instead of using NormalizeColumnVectors(&x_unit_cam).
+ *x_camera = x_unit_cam.block(0, 0, 2, num_points);
+ for (int i = 0; i < num_points; ++i) {
+ x_unit_cam.col(i).normalize();
+ }
+
+ // Create the 3D points in the camera system.
+ Mat X_camera(3, num_points);
+ for (int i = 0; i < num_points; ++i) {
+ X_camera.col(i) = X_distances(i) * x_unit_cam.col(i);
+ }
+
+ // Apply the transformation to the camera 3D points
+ Mat translation_matrix(3, num_points);
+ translation_matrix.row(0).setConstant(T_input(0));
+ translation_matrix.row(1).setConstant(T_input(1));
+ translation_matrix.row(2).setConstant(T_input(2));
+
+ *X_world = R_input * X_camera + translation_matrix;
+
+ // Create the expected result for comparison.
+ *R_expected = R_input.transpose();
+ *T_expected = *R_expected * (-T_input);
+};
+
+TEST(AbsoluteOrientation, QuaternionSolution) {
+ int num_points = 4;
+ Mat X;
+ Mat Xp;
+ X = 100 * Mat::Random(3, num_points);
+
+ // Create a random translation and rotation.
+ Mat3 R_input;
+ R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ())
+ * Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY())
+ * Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
+
+ Vec3 t_input;
+ t_input.setRandom();
+ t_input = 100 * t_input;
+
+ Mat translation_matrix(3, num_points);
+ translation_matrix.row(0).setConstant(t_input(0));
+ translation_matrix.row(1).setConstant(t_input(1));
+ translation_matrix.row(2).setConstant(t_input(2));
+
+ // Create the transformed 3D points Xp as Xp = R * X + t.
+ Xp = R_input * X + translation_matrix;
+
+ // Output variables.
+ Mat3 R;
+ Vec3 t;
+
+ AbsoluteOrientation(X, Xp, &R, &t);
+
+ EXPECT_MATRIX_NEAR(t, t_input, 1e-6);
+ EXPECT_MATRIX_NEAR(R, R_input, 1e-8);
+}
+
+TEST(EuclideanResection, Points4KnownImagePointsRandomTranslationRotation) {
+ // In this test only the translation and rotation are random. The image
+ // points are selected from a real case and are well conditioned.
+ Vec2i image_dimensions;
+ image_dimensions << 1600, 1200;
+
+ Mat3 KK;
+ KK << 2796, 0, 804,
+ 0 , 2796, 641,
+ 0, 0, 1;
+
+ // The real image points.
+ int num_points = 4;
+ Mat3X x_image(3, num_points);
+ x_image << 1164.06, 734.948, 749.599, 430.727,
+ 681.386, 844.59, 496.315, 580.775,
+ 1, 1, 1, 1;
+
+
+ // A vector of the 4 distances to the 3D points.
+ Vec X_distances = 100 * Vec::Random(num_points).array().abs();
+
+ // Create the random camera motion R and t that resection should recover.
+ Mat3 R_input;
+ R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ())
+ * Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY())
+ * Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
+
+ Vec3 T_input;
+ T_input.setRandom();
+ T_input = 100 * T_input;
+
+ // Create the camera system, also getting the expected result of the
+ // transformation.
+ Mat3 R_expected;
+ Vec3 T_expected;
+ Mat3X X_world;
+ Mat2X x_camera;
+ CreateCameraSystem(KK, x_image, X_distances, R_input, T_input,
+ &x_camera, &X_world, &R_expected, &T_expected);
+
+ // Finally, run the code under test.
+ Mat3 R_output;
+ Vec3 T_output;
+ EuclideanResection(x_camera, X_world,
+ &R_output, &T_output,
+ RESECTION_ANSAR_DANIILIDIS);
+
+ EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
+ EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
+
+ // For now, the EPnP doesn't have a non-linear optimization step and so is
+ // not precise enough with only 4 points.
+ //
+ // TODO(jmichot): Reenable this test when there is nonlinear refinement.
+#if 0
+ R_output.setIdentity();
+ T_output.setZero();
+
+ EuclideanResection(x_camera, X_world,
+ &R_output, &T_output,
+ RESECTION_EPNP);
+
+ EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
+ EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);*/
+#endif
+}
+
+// TODO(jmichot): Reduce the code duplication here with the code above.
+TEST(EuclideanResection, Points6AllRandomInput) {
+ Mat3 KK;
+ KK << 2796, 0, 804,
+ 0 , 2796, 641,
+ 0, 0, 1;
+
+ // Create random image points for a 1600x1200 image.
+ int w = 1600;
+ int h = 1200;
+ int num_points = 6;
+ Mat3X x_image(3, num_points);
+ x_image.row(0) = w * Vec::Random(num_points).array().abs();
+ x_image.row(1) = h * Vec::Random(num_points).array().abs();
+ x_image.row(2).setOnes();
+
+ // Normalized camera coordinates to be used as an input to the PnP function.
+ Mat2X x_camera;
+ Vec X_distances = 100 * Vec::Random(num_points).array().abs();
+
+ // Create the random camera motion R and t that resection should recover.
+ Mat3 R_input;
+ R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ())
+ * Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY())
+ * Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
+
+ Vec3 T_input;
+ T_input.setRandom();
+ T_input = 100 * T_input;
+
+ // Create the camera system.
+ Mat3 R_expected;
+ Vec3 T_expected;
+ Mat3X X_world;
+ CreateCameraSystem(KK, x_image, X_distances, R_input, T_input,
+ &x_camera, &X_world, &R_expected, &T_expected);
+
+ // Test each of the resection methods.
+ {
+ Mat3 R_output;
+ Vec3 T_output;
+ EuclideanResection(x_camera, X_world,
+ &R_output, &T_output,
+ RESECTION_ANSAR_DANIILIDIS);
+ EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
+ EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
+ }
+ {
+ Mat3 R_output;
+ Vec3 T_output;
+ EuclideanResection(x_camera, X_world,
+ &R_output, &T_output,
+ RESECTION_EPNP);
+ EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
+ EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
+ }
+ {
+ Mat3 R_output;
+ Vec3 T_output;
+ EuclideanResection(x_image, X_world, KK,
+ &R_output, &T_output);
+ EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
+ EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
+ }
+}
+#endif
diff --git a/extern/libmv/libmv/tracking/brute_region_tracker_test.cc b/extern/libmv/libmv/tracking/brute_region_tracker_test.cc
new file mode 100644
index 00000000000..9014797c7cf
--- /dev/null
+++ b/extern/libmv/libmv/tracking/brute_region_tracker_test.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2011 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/tracking/brute_region_tracker.h"
+#include "libmv/image/image.h"
+#include "libmv/logging/logging.h"
+#include "testing/testing.h"
+
+namespace libmv {
+namespace {
+
+TEST(KltRegionTracker, Track) {
+ Array3Df image1(51, 51);
+ image1.Fill(0);
+
+ Array3Df image2(image1);
+
+ int x0 = 25, y0 = 25;
+ int dx = 3, dy = 2;
+ image1(y0, x0) = 1.0f;
+ image2(y0 + dy, x0 + dx) = 1.0;
+
+ double x1 = x0;
+ double y1 = y0;
+
+ BruteRegionTracker tracker;
+ EXPECT_TRUE(tracker.Track(image1, image2, x0, y0, &x1, &y1));
+
+ EXPECT_NEAR(x1, x0 + dx, 0.001);
+ EXPECT_NEAR(y1, y0 + dy, 0.001);
+}
+
+} // namespace
+} // namespace libmv
diff --git a/extern/libmv/libmv/tracking/klt_region_tracker_test.cc b/extern/libmv/libmv/tracking/klt_region_tracker_test.cc
new file mode 100644
index 00000000000..07d5d6500e3
--- /dev/null
+++ b/extern/libmv/libmv/tracking/klt_region_tracker_test.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2011 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/tracking/klt_region_tracker.h"
+#include "libmv/image/image.h"
+#include "testing/testing.h"
+
+namespace libmv {
+namespace {
+
+TEST(KltRegionTracker, Track) {
+ Array3Df image1(51, 51);
+ image1.Fill(0);
+
+ Array3Df image2(image1);
+
+ int x0 = 25, y0 = 25;
+ int dx = 3, dy = 2;
+ image1(y0, x0) = 1.0f;
+ image2(y0 + dy, x0 + dx) = 1.0f;
+
+ double x1 = x0;
+ double y1 = y0;
+
+ KltRegionTracker tracker;
+ tracker.half_window_size = 6;
+ EXPECT_TRUE(tracker.Track(image1, image2, x0, y0, &x1, &y1));
+
+ EXPECT_NEAR(x1, x0 + dx, 0.001);
+ EXPECT_NEAR(y1, y0 + dy, 0.001);
+}
+
+} // namespace
+} // namespace libmv
diff --git a/extern/libmv/libmv/tracking/pyramid_region_tracker_test.cc b/extern/libmv/libmv/tracking/pyramid_region_tracker_test.cc
new file mode 100644
index 00000000000..d90a1012237
--- /dev/null
+++ b/extern/libmv/libmv/tracking/pyramid_region_tracker_test.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2011 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "libmv/tracking/pyramid_region_tracker.h"
+#include "libmv/tracking/klt_region_tracker.h"
+#include "libmv/image/image.h"
+#include "testing/testing.h"
+
+namespace libmv {
+namespace {
+
+TEST(PyramidKltRegionTracker, Track) {
+ Array3Df image1(100, 100);
+ image1.Fill(0);
+
+ Array3Df image2(image1);
+
+ int x1 = 25, y1 = 25;
+ image1(y1 + 0, x1 + 0) = 1.0f;
+ image1(y1 + 0, x1 + 1) = 1.0f;
+ image1(y1 + 1, x1 + 0) = 1.0f;
+ image1(y1 + 1, x1 + 1) = 1.0f;
+
+ // Make the displacement too large for a single-level KLT.
+ int x2 = x1 + 6, y2 = y1 + 5;
+ image2(y2 + 0, x2 + 0) = 1.0f;
+ image2(y2 + 0, x2 + 1) = 1.0f;
+ image2(y2 + 1, x2 + 0) = 1.0f;
+ image2(y2 + 1, x2 + 1) = 1.0f;
+
+ // Use a small 5x5 tracking region.
+ int half_window_size = 3;
+
+ // Ensure that the track doesn't work with one level of KLT.
+ {
+ double x2_actual = x1;
+ double y2_actual = y1;
+
+ KltRegionTracker tracker;
+ tracker.half_window_size = half_window_size;
+ EXPECT_FALSE(tracker.Track(image1, image2, x1, y1,
+ &x2_actual, &y2_actual));
+ }
+
+ // Verify that it works with the pyramid tracker.
+ {
+ double x2_actual = x1;
+ double y2_actual = y1;
+
+ KltRegionTracker *klt_tracker = new KltRegionTracker;
+ klt_tracker->half_window_size = half_window_size;
+
+ PyramidRegionTracker tracker(klt_tracker, 3);
+ EXPECT_TRUE(tracker.Track(image1, image2, x1, y1,
+ &x2_actual, &y2_actual));
+
+ EXPECT_NEAR(x2_actual, x2, 0.001);
+ EXPECT_NEAR(y2_actual, y2, 0.001);
+ }
+}
+
+} // namespace
+} // namespace libmv
diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt
index a1b0030491e..efd5ec61525 100644
--- a/intern/cycles/CMakeLists.txt
+++ b/intern/cycles/CMakeLists.txt
@@ -20,8 +20,10 @@ if(WIN32 AND MSVC)
# /arch:AVX for VC2012 and above
if(NOT MSVC_VERSION LESS 1700)
set(CYCLES_AVX_ARCH_FLAGS "/arch:AVX")
+ set(CYCLES_AVX2_ARCH_FLAGS "/arch:AVX /arch:AVX2")
elseif(NOT CMAKE_CL_64)
set(CYCLES_AVX_ARCH_FLAGS "/arch:SSE2")
+ set(CYCLES_AVX2_ARCH_FLAGS "/arch:SSE2")
endif()
# there is no /arch:SSE3, but intrinsics are available anyway
@@ -30,11 +32,13 @@ if(WIN32 AND MSVC)
set(CYCLES_SSE3_KERNEL_FLAGS "/fp:fast -D_CRT_SECURE_NO_WARNINGS /GS-")
set(CYCLES_SSE41_KERNEL_FLAGS "/fp:fast -D_CRT_SECURE_NO_WARNINGS /GS-")
set(CYCLES_AVX_KERNEL_FLAGS "${CYCLES_AVX_ARCH_FLAGS} /fp:fast -D_CRT_SECURE_NO_WARNINGS /GS-")
+ set(CYCLES_AVX2_KERNEL_FLAGS "${CYCLES_AVX2_ARCH_FLAGS} /fp:fast -D_CRT_SECURE_NO_WARNINGS /GS-")
else()
set(CYCLES_SSE2_KERNEL_FLAGS "/arch:SSE2 /fp:fast -D_CRT_SECURE_NO_WARNINGS /GS-")
set(CYCLES_SSE3_KERNEL_FLAGS "/arch:SSE2 /fp:fast -D_CRT_SECURE_NO_WARNINGS /GS-")
set(CYCLES_SSE41_KERNEL_FLAGS "/arch:SSE2 /fp:fast -D_CRT_SECURE_NO_WARNINGS /GS-")
set(CYCLES_AVX_KERNEL_FLAGS "${CYCLES_AVX_ARCH_FLAGS} /fp:fast -D_CRT_SECURE_NO_WARNINGS /GS-")
+ set(CYCLES_AVX2_KERNEL_FLAGS "${CYCLES_AVX2_ARCH_FLAGS} /fp:fast -D_CRT_SECURE_NO_WARNINGS /GS-")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fp:fast -D_CRT_SECURE_NO_WARNINGS /GS-")
@@ -48,6 +52,7 @@ elseif(CMAKE_COMPILER_IS_GNUCC)
set(CYCLES_SSE3_KERNEL_FLAGS "-ffast-math -msse -msse2 -msse3 -mssse3 -mfpmath=sse")
set(CYCLES_SSE41_KERNEL_FLAGS "-ffast-math -msse -msse2 -msse3 -mssse3 -msse4.1 -mfpmath=sse")
set(CYCLES_AVX_KERNEL_FLAGS "-ffast-math -msse -msse2 -msse3 -mssse3 -msse4.1 -mavx -mfpmath=sse")
+ set(CYCLES_AVX2_KERNEL_FLAGS "-ffast-math -msse -msse2 -msse3 -mssse3 -msse4.1 -mavx -mavx2 -mfma -mlzcnt -mbmi -mbmi2 -mfpmath=sse")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffast-math")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
@@ -57,6 +62,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CYCLES_SSE3_KERNEL_FLAGS "-ffast-math -msse -msse2 -msse3 -mssse3")
set(CYCLES_SSE41_KERNEL_FLAGS "-ffast-math -msse -msse2 -msse3 -mssse3 -msse4.1")
set(CYCLES_AVX_KERNEL_FLAGS "-ffast-math -msse -msse2 -msse3 -mssse3 -msse4.1 -mavx")
+ set(CYCLES_AVX2_KERNEL_FLAGS "-ffast-math -msse -msse2 -msse3 -mssse3 -msse4.1 -mavx -mavx2 -mfma -mlzcnt -mbmi -mbmi2")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffast-math")
endif()
@@ -67,14 +73,16 @@ if(CXX_HAS_SSE)
-DWITH_KERNEL_SSE3
-DWITH_KERNEL_SSE41
-DWITH_KERNEL_AVX
+ -DWITH_KERNEL_AVX2
)
endif()
-# for OSL
-if(WIN32 AND MSVC)
- set(RTTI_DISABLE_FLAGS "/GR- -DBOOST_NO_RTTI -DBOOST_NO_TYPEID")
-elseif(CMAKE_COMPILER_IS_GNUCC OR (CMAKE_C_COMPILER_ID MATCHES "Clang"))
- set(RTTI_DISABLE_FLAGS "-fno-rtti -DBOOST_NO_RTTI -DBOOST_NO_TYPEID")
+if(WITH_CYCLES_OSL)
+ if(WIN32 AND MSVC)
+ set(RTTI_DISABLE_FLAGS "/GR- -DBOOST_NO_RTTI -DBOOST_NO_TYPEID")
+ elseif(CMAKE_COMPILER_IS_GNUCC OR (CMAKE_C_COMPILER_ID MATCHES "Clang"))
+ set(RTTI_DISABLE_FLAGS "-fno-rtti -DBOOST_NO_RTTI -DBOOST_NO_TYPEID")
+ endif()
endif()
# Definitions and Includes
diff --git a/intern/cycles/SConscript b/intern/cycles/SConscript
index 542bb82cf2a..00acde28560 100644
--- a/intern/cycles/SConscript
+++ b/intern/cycles/SConscript
@@ -39,6 +39,7 @@ sources.remove(path.join('kernel', 'kernel_sse2.cpp'))
sources.remove(path.join('kernel', 'kernel_sse3.cpp'))
sources.remove(path.join('kernel', 'kernel_sse41.cpp'))
sources.remove(path.join('kernel', 'kernel_avx.cpp'))
+sources.remove(path.join('kernel', 'kernel_avx2.cpp'))
incs = []
defs = []
@@ -98,6 +99,7 @@ elif env['OURPLATFORM'] == 'win64-vc':
if env['MSVC_VERSION'] >= '12.0':
kernel_flags['sse41'] = kernel_flags['sse3']
kernel_flags['avx'] = kernel_flags['sse41'] + ' /arch:AVX'
+ kernel_flags['avx2'] = kernel_flags['sse41'] + ' /arch:AVX /arch:AVX2'
else:
# -mavx only available with relatively new gcc/clang
kernel_flags['sse2'] = '-ffast-math -msse -msse2 -mfpmath=sse'
@@ -106,6 +108,7 @@ else:
if (env['C_COMPILER_ID'] == 'gcc' and env['CCVERSION'] >= '4.6') or (env['C_COMPILER_ID'] == 'clang' and env['CCVERSION'] >= '3.1'):
kernel_flags['avx'] = kernel_flags['sse41'] + ' -mavx'
+ kernel_flags['avx2'] = kernel_flags['avx'] + ' -mavx2 -mfma -mlzcnt -mbmi -mbmi2'
for kernel_type in kernel_flags.keys():
defs.append('WITH_KERNEL_' + kernel_type.upper())
diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp
index de554fba954..915ef96a517 100644
--- a/intern/cycles/app/cycles_xml.cpp
+++ b/intern/cycles/app/cycles_xml.cpp
@@ -509,8 +509,10 @@ static void xml_read_shader_graph(const XMLReadState& state, Shader *shader, pug
else if(string_iequals(node.name(), "mapping")) {
snode = new MappingNode();
}
- else if(string_iequals(node.name(), "ward_bsdf")) {
- snode = new WardBsdfNode();
+ else if(string_iequals(node.name(), "anisotropic_bsdf")) {
+ AnisotropicBsdfNode *aniso = new AnisotropicBsdfNode();
+ xml_read_enum(&aniso->distribution, AnisotropicBsdfNode::distribution_enum, node, "distribution");
+ snode = aniso;
}
else if(string_iequals(node.name(), "diffuse_bsdf")) {
snode = new DiffuseBsdfNode();
@@ -633,6 +635,12 @@ static void xml_read_shader_graph(const XMLReadState& state, Shader *shader, pug
else if(string_iequals(node.name(), "separate_hsv")) {
snode = new SeparateHSVNode();
}
+ else if(string_iequals(node.name(), "combine_xyz")) {
+ snode = new CombineHSVNode();
+ }
+ else if(string_iequals(node.name(), "separate_xyz")) {
+ snode = new SeparateHSVNode();
+ }
else if(string_iequals(node.name(), "hsv")) {
snode = new HSVNode();
}
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index 7205a272395..b4a1b10f8b4 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -108,9 +108,10 @@ enum_integrator = (
('PATH', "Path Tracing", "Pure path tracing integrator"),
)
-enum_volume_homogeneous_sampling = (
- ('DISTANCE', "Distance", "Use Distance Sampling"),
- ('EQUI_ANGULAR', "Equi-angular", "Use Equi-angular Sampling"),
+enum_volume_sampling = (
+ ('DISTANCE', "Distance", "Use distance sampling, best for dense volumes with lights far away"),
+ ('EQUIANGULAR', "Equiangular", "Use equiangular sampling, best for volumes with low density with light inside or near the volume"),
+ ('MULTIPLE_IMPORTANCE', "Multiple Importance", "Combine distance and equi-angular sampling for volumes where neither method is ideal"),
)
@@ -146,13 +147,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
default='PATH',
)
- cls.volume_homogeneous_sampling = EnumProperty(
- name="Homogeneous Sampling",
- description="Sampling method to use for homogeneous volumes",
- items=enum_volume_homogeneous_sampling,
- default='DISTANCE',
- )
-
cls.use_square_samples = BoolProperty(
name="Square Samples",
description="Square sampling values for easier artist control",
@@ -236,7 +230,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
name="Volume Samples",
description="Number of volume scattering samples to render for each AA sample",
min=1, max=10000,
- default=1,
+ default=0,
)
cls.sampling_pattern = EnumProperty(
@@ -315,7 +309,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
name="Volume Bounces",
description="Maximum number of volumetric scattering events",
min=0, max=1024,
- default=1,
+ default=0,
)
cls.transparent_min_bounces = IntProperty(
@@ -602,6 +596,12 @@ class CyclesMaterialSettings(bpy.types.PropertyGroup):
"(not using any textures), for faster rendering",
default=False,
)
+ cls.volume_sampling = EnumProperty(
+ name="Volume Sampling",
+ description="Sampling method to use for volumes",
+ items=enum_volume_sampling,
+ default='DISTANCE',
+ )
@classmethod
def unregister(cls):
@@ -672,6 +672,12 @@ class CyclesWorldSettings(bpy.types.PropertyGroup):
"(not using any textures), for faster rendering",
default=False,
)
+ cls.volume_sampling = EnumProperty(
+ name="Volume Sampling",
+ description="Sampling method to use for volumes",
+ items=enum_volume_sampling,
+ default='EQUIANGULAR',
+ )
@classmethod
def unregister(cls):
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index 5c8115b6612..9dd5a4e81ce 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -176,16 +176,11 @@ class CyclesRender_PT_volume_sampling(CyclesButtonsPanel, Panel):
scene = context.scene
cscene = scene.cycles
- split = layout.split(align=True)
-
- sub = split.column(align=True)
- sub.label("Heterogeneous:")
- sub.prop(cscene, "volume_step_size")
- sub.prop(cscene, "volume_max_steps")
-
- sub = split.column(align=True)
- sub.label("Homogeneous:")
- sub.prop(cscene, "volume_homogeneous_sampling", text="")
+ row = layout.row()
+ row.label("Heterogeneous:")
+ row = layout.row()
+ row.prop(cscene, "volume_step_size")
+ row.prop(cscene, "volume_max_steps")
class CyclesRender_PT_light_paths(CyclesButtonsPanel, Panel):
@@ -829,8 +824,6 @@ class CyclesWorld_PT_volume(CyclesButtonsPanel, Panel):
world = context.world
panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Volume')
- layout.prop(world.cycles, "homogeneous_volume")
-
class CyclesWorld_PT_ambient_occlusion(CyclesButtonsPanel, Panel):
bl_label = "Ambient Occlusion"
@@ -922,15 +915,24 @@ class CyclesWorld_PT_settings(CyclesButtonsPanel, Panel):
cworld = world.cycles
cscene = context.scene.cycles
- col = layout.column()
+ split = layout.split()
+
+ col = split.column()
+
+ col.label(text="Surface:")
+ col.prop(cworld, "sample_as_light", text="Multiple Importance")
- col.prop(cworld, "sample_as_light")
- sub = col.row(align=True)
+ sub = col.column(align=True)
sub.active = cworld.sample_as_light
sub.prop(cworld, "sample_map_resolution")
if cscene.progressive == 'BRANCHED_PATH':
sub.prop(cworld, "samples")
+ col = split.column()
+ col.label(text="Volume:")
+ col.prop(cworld, "volume_sampling", text="")
+ col.prop(cworld, "homogeneous_volume", text="Homogeneous")
+
class CyclesMaterial_PT_preview(CyclesButtonsPanel, Panel):
bl_label = "Preview"
@@ -979,8 +981,6 @@ class CyclesMaterial_PT_volume(CyclesButtonsPanel, Panel):
panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Volume')
- layout.prop(cmat, "homogeneous_volume")
-
class CyclesMaterial_PT_displacement(CyclesButtonsPanel, Panel):
bl_label = "Displacement"
@@ -1023,10 +1023,18 @@ class CyclesMaterial_PT_settings(CyclesButtonsPanel, Panel):
col.label()
col.prop(mat, "pass_index")
- col = layout.column()
- col.prop(cmat, "sample_as_light")
+ split = layout.split()
+
+ col = split.column()
+ col.label(text="Surface:")
+ col.prop(cmat, "sample_as_light", text="Multiple Importance")
col.prop(cmat, "use_transparent_shadow")
+ col = split.column()
+ col.label(text="Volume:")
+ col.prop(cmat, "volume_sampling", text="")
+ col.prop(cmat, "homogeneous_volume", text="Homogeneous")
+
class CyclesTexture_PT_context(CyclesButtonsPanel, Panel):
bl_label = ""
@@ -1259,8 +1267,13 @@ class CyclesRender_PT_bake(CyclesButtonsPanel, Panel):
sub = sub.column()
sub.active = cbk.use_selected_to_active
- sub.prop(cbk, "cage_extrusion", text="Distance")
- sub.prop_search(cbk, "cage", scene, "objects")
+
+ sub.prop(cbk, "use_cage", text="Cage")
+ if cbk.use_cage:
+ sub.prop(cbk, "cage_extrusion", text="Cage Extrusion")
+ sub.prop_search(cbk, "cage_object", scene, "objects")
+ else:
+ sub.prop(cbk, "cage_extrusion", text="Ray Distance")
if cscene.bake_type == 'NORMAL':
col.separator()
diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp
index 22de7b64273..7b1a8ec0b15 100644
--- a/intern/cycles/blender/blender_curves.cpp
+++ b/intern/cycles/blender/blender_curves.cpp
@@ -42,7 +42,7 @@ void ExportCurveSegments(Scene *scene, Mesh *mesh, ParticleCurveData *CData);
void ExportCurveTrianglePlanes(Mesh *mesh, ParticleCurveData *CData, float3 RotCam);
void ExportCurveTriangleGeometry(Mesh *mesh, ParticleCurveData *CData, int resolution);
void ExportCurveTriangleUV(Mesh *mesh, ParticleCurveData *CData, int vert_offset, int resol, float3 *uvdata);
-void ExportCurveTriangleVcol(Mesh *mesh, ParticleCurveData *CData, int vert_offset, int resol, float3 *fdata);
+void ExportCurveTriangleVcol(Mesh *mesh, ParticleCurveData *CData, int vert_offset, int resol, uchar4 *cdata);
ParticleCurveData::ParticleCurveData()
{
@@ -726,9 +726,9 @@ void ExportCurveTriangleUV(Mesh *mesh, ParticleCurveData *CData, int vert_offset
}
}
-void ExportCurveTriangleVcol(Mesh *mesh, ParticleCurveData *CData, int vert_offset, int resol, float3 *fdata)
+void ExportCurveTriangleVcol(Mesh *mesh, ParticleCurveData *CData, int vert_offset, int resol, uchar4 *cdata)
{
- if(fdata == NULL)
+ if(cdata == NULL)
return;
int vertexindex = vert_offset;
@@ -740,17 +740,17 @@ void ExportCurveTriangleVcol(Mesh *mesh, ParticleCurveData *CData, int vert_offs
for(int curvekey = CData->curve_firstkey[curve]; curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1; curvekey++) {
for(int section = 0; section < resol; section++) {
- fdata[vertexindex] = color_srgb_to_scene_linear(CData->curve_vcol[curve]);
+ cdata[vertexindex] = color_float_to_byte(color_srgb_to_scene_linear(CData->curve_vcol[curve]));
vertexindex++;
- fdata[vertexindex] = color_srgb_to_scene_linear(CData->curve_vcol[curve]);
+ cdata[vertexindex] = color_float_to_byte(color_srgb_to_scene_linear(CData->curve_vcol[curve]));
vertexindex++;
- fdata[vertexindex] = color_srgb_to_scene_linear(CData->curve_vcol[curve]);
+ cdata[vertexindex] = color_float_to_byte(color_srgb_to_scene_linear(CData->curve_vcol[curve]));
vertexindex++;
- fdata[vertexindex] = color_srgb_to_scene_linear(CData->curve_vcol[curve]);
+ cdata[vertexindex] = color_float_to_byte(color_srgb_to_scene_linear(CData->curve_vcol[curve]));
vertexindex++;
- fdata[vertexindex] = color_srgb_to_scene_linear(CData->curve_vcol[curve]);
+ cdata[vertexindex] = color_float_to_byte(color_srgb_to_scene_linear(CData->curve_vcol[curve]));
vertexindex++;
- fdata[vertexindex] = color_srgb_to_scene_linear(CData->curve_vcol[curve]);
+ cdata[vertexindex] = color_float_to_byte(color_srgb_to_scene_linear(CData->curve_vcol[curve]));
vertexindex++;
}
}
@@ -923,13 +923,12 @@ void BlenderSync::sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_ob, bool
ObtainCacheParticleVcol(mesh, &b_mesh, &b_ob, &CData, !preview, vcol_num);
if(primitive == CURVE_TRIANGLES) {
-
Attribute *attr_vcol = mesh->attributes.add(
- ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CORNER);
+ ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CORNER_BYTE);
- float3 *fdata = attr_vcol->data_float3();
+ uchar4 *cdata = attr_vcol->data_uchar4();
- ExportCurveTriangleVcol(mesh, &CData, tri_num * 3, used_res, fdata);
+ ExportCurveTriangleVcol(mesh, &CData, tri_num * 3, used_res, cdata);
}
else {
Attribute *attr_vcol = mesh->curve_attributes.add(
diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp
index 27ea552c6a7..e7c18c9706b 100644
--- a/intern/cycles/blender/blender_mesh.cpp
+++ b/intern/cycles/blender/blender_mesh.cpp
@@ -208,7 +208,7 @@ static void mikk_compute_tangents(BL::Mesh b_mesh, BL::MeshTextureFaceLayer b_la
/* Create Volume Attribute */
-static void create_mesh_volume_attribute(BL::Object b_ob, Mesh *mesh, ImageManager *image_manager, AttributeStandard std)
+static void create_mesh_volume_attribute(BL::Object b_ob, Mesh *mesh, ImageManager *image_manager, AttributeStandard std, float frame)
{
BL::SmokeDomainSettings b_domain = object_smoke_domain_find(b_ob);
@@ -222,22 +222,22 @@ static void create_mesh_volume_attribute(BL::Object b_ob, Mesh *mesh, ImageManag
volume_data->manager = image_manager;
volume_data->slot = image_manager->add_image(Attribute::standard_name(std),
- b_ob.ptr.data, animated, is_float, is_linear, INTERPOLATION_LINEAR, true);
+ b_ob.ptr.data, animated, frame, is_float, is_linear, INTERPOLATION_LINEAR, true);
}
-static void create_mesh_volume_attributes(Scene *scene, BL::Object b_ob, Mesh *mesh)
+static void create_mesh_volume_attributes(Scene *scene, BL::Object b_ob, Mesh *mesh, float frame)
{
/* for smoke volume rendering */
if(mesh->need_attribute(scene, ATTR_STD_VOLUME_DENSITY))
- create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_DENSITY);
+ create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_DENSITY, frame);
if(mesh->need_attribute(scene, ATTR_STD_VOLUME_COLOR))
- create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_COLOR);
+ create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_COLOR, frame);
if(mesh->need_attribute(scene, ATTR_STD_VOLUME_FLAME))
- create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_FLAME);
+ create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_FLAME, frame);
if(mesh->need_attribute(scene, ATTR_STD_VOLUME_HEAT))
- create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_HEAT);
+ create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_HEAT, frame);
if(mesh->need_attribute(scene, ATTR_STD_VOLUME_VELOCITY))
- create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_VELOCITY);
+ create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_VELOCITY, frame);
}
/* Create Mesh */
@@ -347,25 +347,25 @@ static void create_mesh(Scene *scene, Mesh *mesh, BL::Mesh b_mesh, const vector<
continue;
Attribute *attr = mesh->attributes.add(
- ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CORNER);
+ ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CORNER_BYTE);
BL::MeshColorLayer::data_iterator c;
- float3 *fdata = attr->data_float3();
+ uchar4 *cdata = attr->data_uchar4();
size_t i = 0;
for(l->data.begin(c); c != l->data.end(); ++c, ++i) {
- fdata[0] = color_srgb_to_scene_linear(get_float3(c->color1()));
- fdata[1] = color_srgb_to_scene_linear(get_float3(c->color2()));
- fdata[2] = color_srgb_to_scene_linear(get_float3(c->color3()));
+ cdata[0] = color_float_to_byte(color_srgb_to_scene_linear(get_float3(c->color1())));
+ cdata[1] = color_float_to_byte(color_srgb_to_scene_linear(get_float3(c->color2())));
+ cdata[2] = color_float_to_byte(color_srgb_to_scene_linear(get_float3(c->color3())));
if(nverts[i] == 4) {
- fdata[3] = fdata[0];
- fdata[4] = fdata[2];
- fdata[5] = color_srgb_to_scene_linear(get_float3(c->color4()));
- fdata += 6;
+ cdata[3] = cdata[0];
+ cdata[4] = cdata[2];
+ cdata[5] = color_float_to_byte(color_srgb_to_scene_linear(get_float3(c->color4())));
+ cdata += 6;
}
else
- fdata += 3;
+ cdata += 3;
}
}
}
@@ -561,7 +561,7 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tri
else
create_mesh(scene, mesh, b_mesh, used_shaders);
- create_mesh_volume_attributes(scene, b_ob, mesh);
+ create_mesh_volume_attributes(scene, b_ob, mesh, b_scene.frame_current());
}
if(render_layer.use_hair)
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
index 379e27c558c..51d8a4da744 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -244,6 +244,12 @@ static ShaderNode *add_node(Scene *scene, BL::BlendData b_data, BL::Scene b_scen
else if (b_node.is_a(&RNA_ShaderNodeCombineHSV)) {
node = new CombineHSVNode();
}
+ else if (b_node.is_a(&RNA_ShaderNodeSeparateXYZ)) {
+ node = new SeparateXYZNode();
+ }
+ else if (b_node.is_a(&RNA_ShaderNodeCombineXYZ)) {
+ node = new CombineXYZNode();
+ }
else if (b_node.is_a(&RNA_ShaderNodeHueSaturation)) {
node = new HSVNode();
}
@@ -312,7 +318,23 @@ static ShaderNode *add_node(Scene *scene, BL::BlendData b_data, BL::Scene b_scen
node = new HoldoutNode();
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfAnisotropic)) {
- node = new WardBsdfNode();
+ BL::ShaderNodeBsdfAnisotropic b_aniso_node(b_node);
+ AnisotropicBsdfNode *aniso = new AnisotropicBsdfNode();
+
+ switch (b_aniso_node.distribution())
+ {
+ case BL::ShaderNodeBsdfAnisotropic::distribution_BECKMANN:
+ aniso->distribution = ustring("Beckmann");
+ break;
+ case BL::ShaderNodeBsdfAnisotropic::distribution_GGX:
+ aniso->distribution = ustring("GGX");
+ break;
+ case BL::ShaderNodeBsdfAnisotropic::distribution_ASHIKHMIN_SHIRLEY:
+ aniso->distribution = ustring("Ashikhmin-Shirley");
+ break;
+ }
+
+ node = aniso;
}
else if (b_node.is_a(&RNA_ShaderNodeBsdfDiffuse)) {
node = new DiffuseBsdfNode();
@@ -347,6 +369,9 @@ static ShaderNode *add_node(Scene *scene, BL::BlendData b_data, BL::Scene b_scen
case BL::ShaderNodeBsdfGlossy::distribution_GGX:
glossy->distribution = ustring("GGX");
break;
+ case BL::ShaderNodeBsdfGlossy::distribution_ASHIKHMIN_SHIRLEY:
+ glossy->distribution = ustring("Ashikhmin-Shirley");
+ break;
}
node = glossy;
}
@@ -971,6 +996,7 @@ void BlenderSync::sync_materials(bool update_all)
shader->use_mis = get_boolean(cmat, "sample_as_light");
shader->use_transparent_shadow = get_boolean(cmat, "use_transparent_shadow");
shader->heterogeneous_volume = !get_boolean(cmat, "homogeneous_volume");
+ shader->volume_sampling_method = RNA_enum_get(&cmat, "volume_sampling");
shader->set_graph(graph);
shader->tag_update(scene);
@@ -1000,6 +1026,7 @@ void BlenderSync::sync_world(bool update_all)
/* volume */
PointerRNA cworld = RNA_pointer_get(&b_world.ptr, "cycles");
shader->heterogeneous_volume = !get_boolean(cworld, "homogeneous_volume");
+ shader->volume_sampling_method = RNA_enum_get(&cworld, "volume_sampling");
}
else if(b_world) {
ShaderNode *closure, *out;
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index bdb3e20632f..042bbca9497 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -177,7 +177,6 @@ void BlenderSync::sync_integrator()
integrator->transparent_min_bounce = get_int(cscene, "transparent_min_bounces");
integrator->transparent_shadows = get_boolean(cscene, "use_transparent_shadows");
- integrator->volume_homogeneous_sampling = RNA_enum_get(&cscene, "volume_homogeneous_sampling");
integrator->volume_max_steps = get_int(cscene, "volume_max_steps");
integrator->volume_step_size = get_float(cscene, "volume_step_size");
diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp
index 71bf2d23d6e..7308d036fe3 100644
--- a/intern/cycles/device/device_cpu.cpp
+++ b/intern/cycles/device/device_cpu.cpp
@@ -62,6 +62,7 @@ public:
system_cpu_support_sse3();
system_cpu_support_sse41();
system_cpu_support_avx();
+ system_cpu_support_avx2();
}
~CPUDevice()
@@ -167,6 +168,28 @@ public:
int start_sample = tile.start_sample;
int end_sample = tile.start_sample + tile.num_samples;
+#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2
+ if(system_cpu_support_avx2()) {
+ for(int sample = start_sample; sample < end_sample; sample++) {
+ if (task.get_cancel() || task_pool.canceled()) {
+ if(task.need_finish_queue == false)
+ break;
+ }
+
+ for(int y = tile.y; y < tile.y + tile.h; y++) {
+ for(int x = tile.x; x < tile.x + tile.w; x++) {
+ kernel_cpu_avx2_path_trace(&kg, render_buffer, rng_state,
+ sample, x, y, tile.offset, tile.stride);
+ }
+ }
+
+ tile.sample = sample + 1;
+
+ task.update_progress(tile);
+ }
+ }
+ else
+#endif
#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX
if(system_cpu_support_avx()) {
for(int sample = start_sample; sample < end_sample; sample++) {
@@ -293,6 +316,15 @@ public:
float sample_scale = 1.0f/(task.sample + 1);
if(task.rgba_half) {
+#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2
+ if(system_cpu_support_avx2()) {
+ for(int y = task.y; y < task.y + task.h; y++)
+ for(int x = task.x; x < task.x + task.w; x++)
+ kernel_cpu_avx2_convert_to_half_float(&kernel_globals, (uchar4*)task.rgba_half, (float*)task.buffer,
+ sample_scale, x, y, task.offset, task.stride);
+ }
+ else
+#endif
#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX
if(system_cpu_support_avx()) {
for(int y = task.y; y < task.y + task.h; y++)
@@ -337,6 +369,15 @@ public:
}
}
else {
+#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2
+ if(system_cpu_support_avx2()) {
+ for(int y = task.y; y < task.y + task.h; y++)
+ for(int x = task.x; x < task.x + task.w; x++)
+ kernel_cpu_avx2_convert_to_byte(&kernel_globals, (uchar4*)task.rgba_byte, (float*)task.buffer,
+ sample_scale, x, y, task.offset, task.stride);
+ }
+ else
+#endif
#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX
if(system_cpu_support_avx()) {
for(int y = task.y; y < task.y + task.h; y++)
@@ -390,6 +431,18 @@ public:
OSLShader::thread_init(&kg, &kernel_globals, &osl_globals);
#endif
+#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2
+ if(system_cpu_support_avx2()) {
+ for(int x = task.shader_x; x < task.shader_x + task.shader_w; x++) {
+ for(int sample = 0; sample < task.num_samples; sample++)
+ kernel_cpu_avx2_shader(&kg, (uint4*)task.shader_input, (float4*)task.shader_output, task.shader_eval_type, x, sample);
+
+ if(task.get_cancel() || task_pool.canceled())
+ break;
+ }
+ }
+ else
+#endif
#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX
if(system_cpu_support_avx()) {
for(int x = task.shader_x; x < task.shader_x + task.shader_w; x++) {
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index 994fba7a1d3..58142eec556 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -35,6 +35,8 @@ set(SRC_HEADERS
kernel_passes.h
kernel_path.h
kernel_path_state.h
+ kernel_path_surface.h
+ kernel_path_volume.h
kernel_projection.h
kernel_random.h
kernel_shader.h
@@ -58,7 +60,7 @@ set(SRC_CLOSURE_HEADERS
closure/bsdf_toon.h
closure/bsdf_transparent.h
closure/bsdf_util.h
- closure/bsdf_ward.h
+ closure/bsdf_ashikhmin_shirley.h
closure/bsdf_westin.h
closure/bsdf_hair.h
closure/bssrdf.h
@@ -95,8 +97,8 @@ set(SRC_SVM_HEADERS
svm/svm_noisetex.h
svm/svm_normal.h
svm/svm_ramp.h
- svm/svm_sepcomb_rgb.h
svm/svm_sepcomb_hsv.h
+ svm/svm_sepcomb_vector.h
svm/svm_sky.h
svm/svm_tex_coord.h
svm/svm_texture.h
@@ -213,12 +215,14 @@ if(CXX_HAS_SSE)
kernel_sse3.cpp
kernel_sse41.cpp
kernel_avx.cpp
+ kernel_avx2.cpp
)
set_source_files_properties(kernel_sse2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE2_KERNEL_FLAGS}")
set_source_files_properties(kernel_sse3.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE3_KERNEL_FLAGS}")
set_source_files_properties(kernel_sse41.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE41_KERNEL_FLAGS}")
set_source_files_properties(kernel_avx.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX_KERNEL_FLAGS}")
+ set_source_files_properties(kernel_avx2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX2_KERNEL_FLAGS}")
endif()
diff --git a/intern/cycles/kernel/SConscript b/intern/cycles/kernel/SConscript
index ef28f7b6d91..cfe12e8533d 100644
--- a/intern/cycles/kernel/SConscript
+++ b/intern/cycles/kernel/SConscript
@@ -30,6 +30,7 @@ import subprocess
import sys
import os
import Blender as B
+import btools
def normpath(path):
return os.path.abspath(os.path.normpath(path))
@@ -64,8 +65,7 @@ if env['WITH_BF_CYCLES_CUDA_BINARIES']:
closure_dir = os.path.join(source_dir, "../closure")
# get CUDA version
- nvcc_pipe = subprocess.Popen([nvcc, "--version"],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- output, erroroutput = nvcc_pipe.communicate()
+ output = btools.get_command_output([nvcc, "--version"])
cuda_major_minor = re.findall(r'release (\d+).(\d+)', output)[0]
cuda_version = int(cuda_major_minor[0])*10 + int(cuda_major_minor[1])
diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h
index 24b54cd9d9e..81c239ea0c9 100644
--- a/intern/cycles/kernel/closure/bsdf.h
+++ b/intern/cycles/kernel/closure/bsdf.h
@@ -24,7 +24,7 @@
#include "../closure/bsdf_refraction.h"
#include "../closure/bsdf_transparent.h"
#ifdef __ANISOTROPIC__
-#include "../closure/bsdf_ward.h"
+#include "../closure/bsdf_ashikhmin_shirley.h"
#endif
#include "../closure/bsdf_westin.h"
#include "../closure/bsdf_toon.h"
@@ -83,18 +83,21 @@ ccl_device int bsdf_sample(KernelGlobals *kg, const ShaderData *sd, const Shader
eval, omega_in, &domega_in->dx, &domega_in->dy, pdf);
break;
case CLOSURE_BSDF_MICROFACET_GGX_ID:
+ case CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID:
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
- label = bsdf_microfacet_ggx_sample(sc, sd->Ng, sd->I, sd->dI.dx, sd->dI.dy, randu, randv,
+ label = bsdf_microfacet_ggx_sample(kg, sc, sd->Ng, sd->I, sd->dI.dx, sd->dI.dy, randu, randv,
eval, omega_in, &domega_in->dx, &domega_in->dy, pdf);
break;
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
+ case CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
- label = bsdf_microfacet_beckmann_sample(sc, sd->Ng, sd->I, sd->dI.dx, sd->dI.dy, randu, randv,
+ label = bsdf_microfacet_beckmann_sample(kg, sc, sd->Ng, sd->I, sd->dI.dx, sd->dI.dy, randu, randv,
eval, omega_in, &domega_in->dx, &domega_in->dy, pdf);
break;
#ifdef __ANISOTROPIC__
- case CLOSURE_BSDF_WARD_ID:
- label = bsdf_ward_sample(sc, sd->Ng, sd->I, sd->dI.dx, sd->dI.dy, randu, randv,
+ case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
+ case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID:
+ label = bsdf_ashikhmin_shirley_sample(sc, sd->Ng, sd->I, sd->dI.dx, sd->dI.dy, randu, randv,
eval, omega_in, &domega_in->dx, &domega_in->dy, pdf);
break;
#endif
@@ -178,16 +181,19 @@ ccl_device float3 bsdf_eval(KernelGlobals *kg, const ShaderData *sd, const Shade
eval = bsdf_transparent_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_MICROFACET_GGX_ID:
+ case CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID:
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
eval = bsdf_microfacet_ggx_eval_reflect(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
+ case CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
eval = bsdf_microfacet_beckmann_eval_reflect(sc, sd->I, omega_in, pdf);
break;
#ifdef __ANISOTROPIC__
- case CLOSURE_BSDF_WARD_ID:
- eval = bsdf_ward_eval_reflect(sc, sd->I, omega_in, pdf);
+ case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
+ case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID:
+ eval = bsdf_ashikhmin_shirley_eval_reflect(sc, sd->I, omega_in, pdf);
break;
#endif
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID:
@@ -245,16 +251,19 @@ ccl_device float3 bsdf_eval(KernelGlobals *kg, const ShaderData *sd, const Shade
eval = bsdf_transparent_eval_transmit(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_MICROFACET_GGX_ID:
+ case CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID:
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
eval = bsdf_microfacet_ggx_eval_transmit(sc, sd->I, omega_in, pdf);
break;
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
+ case CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
eval = bsdf_microfacet_beckmann_eval_transmit(sc, sd->I, omega_in, pdf);
break;
#ifdef __ANISOTROPIC__
- case CLOSURE_BSDF_WARD_ID:
- eval = bsdf_ward_eval_transmit(sc, sd->I, omega_in, pdf);
+ case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
+ case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID:
+ eval = bsdf_ashikhmin_shirley_eval_transmit(sc, sd->I, omega_in, pdf);
break;
#endif
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID:
@@ -330,16 +339,19 @@ ccl_device void bsdf_blur(KernelGlobals *kg, ShaderClosure *sc, float roughness)
bsdf_transparent_blur(sc, roughness);
break;
case CLOSURE_BSDF_MICROFACET_GGX_ID:
+ case CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID:
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
bsdf_microfacet_ggx_blur(sc, roughness);
break;
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
+ case CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID:
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
bsdf_microfacet_beckmann_blur(sc, roughness);
break;
#ifdef __ANISOTROPIC__
- case CLOSURE_BSDF_WARD_ID:
- bsdf_ward_blur(sc, roughness);
+ case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
+ case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID:
+ bsdf_ashikhmin_shirley_blur(sc, roughness);
break;
#endif
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID:
diff --git a/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h b/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h
new file mode 100644
index 00000000000..6a5d0410e01
--- /dev/null
+++ b/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * 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
+ */
+
+#ifndef __BSDF_ASHIKHMIN_SHIRLEY_H__
+#define __BSDF_ASHIKHMIN_SHIRLEY_H__
+
+/*
+ASHIKHMIN SHIRLEY BSDF
+
+Implementation of
+Michael Ashikhmin and Peter Shirley: "An Anisotropic Phong BRDF Model" (2000)
+
+The Fresnel factor is missing to get a separable bsdf (intensity*color), as is
+the case with all other microfacet-based BSDF implementations in Cycles.
+
+Other than that, the implementation directly follows the paper.
+*/
+
+CCL_NAMESPACE_BEGIN
+
+ccl_device int bsdf_ashikhmin_shirley_setup(ShaderClosure *sc)
+{
+ /* store roughness. could already convert to exponent to save some cycles
+ * in eval, but this is more consistent with other bsdfs and shader_blur. */
+ sc->data0 = clamp(sc->data0, 1e-4f, 1.0f);
+ sc->data1 = sc->data0;
+
+ sc->type = CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID;
+ return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_GLOSSY;
+}
+
+ccl_device int bsdf_ashikhmin_shirley_aniso_setup(ShaderClosure *sc)
+{
+ /* store roughness. could already convert to exponent to save some cycles
+ * in eval, but this is more consistent with other bsdfs and shader_blur. */
+ sc->data0 = clamp(sc->data0, 1e-4f, 1.0f);
+ sc->data1 = clamp(sc->data1, 1e-4f, 1.0f);
+
+ sc->type = CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID;
+ return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_GLOSSY;
+}
+
+ccl_device void bsdf_ashikhmin_shirley_blur(ShaderClosure *sc, float roughness)
+{
+ sc->data0 = fmaxf(roughness, sc->data0); /* clamp roughness */
+ sc->data1 = fmaxf(roughness, sc->data1);
+}
+
+ccl_device_inline float bsdf_ashikhmin_shirley_roughness_to_exponent(float roughness)
+{
+ return 2.0f / (roughness*roughness) - 2.0f;
+}
+
+ccl_device float3 bsdf_ashikhmin_shirley_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
+{
+ float3 N = sc->N;
+
+ float NdotI = dot(N, I); /* in Cycles/OSL convention I is omega_out */
+ float NdotO = dot(N, omega_in); /* and consequently we use for O omaga_in ;) */
+
+ float out = 0.0f;
+
+ if (NdotI > 0.0f && NdotO > 0.0f) {
+ NdotI = fmaxf(NdotI, 1e-6f);
+ NdotO = fmaxf(NdotO, 1e-6f);
+ float3 H = normalize(omega_in + I);
+ float HdotI = fmaxf(dot(H, I), 1e-6f);
+ float HdotN = fmaxf(dot(H, N), 1e-6f);
+
+ float pump = 1.0f / fmaxf(1e-6f, (HdotI*fmaxf(NdotO, NdotI))); /* pump from original paper (first derivative disc., but cancels the HdotI in the pdf nicely) */
+ /*float pump = 1.0f / fmaxf(1e-4f, ((NdotO + NdotI) * (NdotO*NdotI))); */ /* pump from d-brdf paper */
+
+ float n_x = bsdf_ashikhmin_shirley_roughness_to_exponent(sc->data0);
+ float n_y = bsdf_ashikhmin_shirley_roughness_to_exponent(sc->data1);
+
+ if (n_x == n_y) { /* => isotropic case */
+ float e = n_x;
+ float lobe = powf(HdotN, e);
+ float norm = (n_x + 1.0f) / (8.0f * M_PI_F);
+
+ out = NdotO * norm * lobe * pump;
+ *pdf = norm * lobe / HdotI; /* this is p_h / 4(H.I) (conversion from 'wh measure' to 'wi measure', eq. 8 in paper) */
+ }
+ else { /* => ANisotropic case */
+ float3 X, Y;
+ make_orthonormals_tangent(N, sc->T, &X, &Y);
+
+ float HdotX = dot(H, X);
+ float HdotY = dot(H, Y);
+ float e = (n_x * HdotX*HdotX + n_y * HdotY*HdotY) / (1.0f - HdotN*HdotN);
+ float lobe = powf(HdotN, e);
+ float norm = sqrtf((n_x + 1.0f)*(n_y + 1.0f)) / (8.0f * M_PI_F);
+
+ out = NdotO * norm * lobe * pump;
+ *pdf = norm * lobe / HdotI;
+ }
+ }
+
+ return make_float3(out, out, out);
+}
+
+ccl_device float3 bsdf_ashikhmin_shirley_eval_transmit(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
+{
+ return make_float3(0.0f, 0.0f, 0.0f);
+}
+
+ccl_device_inline void bsdf_ashikhmin_shirley_sample_first_quadrant(float n_x, float n_y, float randu, float randv, float *phi, float *cos_theta)
+{
+ *phi = atanf(sqrtf((n_x + 1.0f) / (n_y + 1.0f)) * tanf(M_PI_2_F * randu));
+ float cos_phi = cosf(*phi);
+ float sin_phi = sinf(*phi);
+ *cos_theta = powf(randv, 1.0f / (n_x * cos_phi*cos_phi + n_y * sin_phi*sin_phi + 1.0f));
+}
+
+ccl_device int bsdf_ashikhmin_shirley_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
+{
+ float3 N = sc->N;
+
+ float NdotI = dot(N, I);
+ if (NdotI > 0.0f) {
+
+ float n_x = bsdf_ashikhmin_shirley_roughness_to_exponent(sc->data0);
+ float n_y = bsdf_ashikhmin_shirley_roughness_to_exponent(sc->data1);
+
+ /* get x,y basis on the surface for anisotropy */
+ float3 X, Y;
+
+ if(n_x == n_y)
+ make_orthonormals(N, &X, &Y);
+ else
+ make_orthonormals_tangent(N, sc->T, &X, &Y);
+
+ /* sample spherical coords for h in tangent space */
+ float phi;
+ float cos_theta;
+ if (n_x == n_y) { /* => simple isotropic sampling */
+ phi = M_2PI_F * randu;
+ cos_theta = powf(randv, 1.0f / (n_x + 1.0f));
+ }
+ else { /* => more complex anisotropic sampling */
+ if (randu < 0.25f) { /* first quadrant */
+ float remapped_randu = 4.0f * randu;
+ bsdf_ashikhmin_shirley_sample_first_quadrant(n_x, n_y, remapped_randu, randv, &phi, &cos_theta);
+ }
+ else if (randu < 0.5f) { /* second quadrant */
+ float remapped_randu = 4.0f * (.5f - randu);
+ bsdf_ashikhmin_shirley_sample_first_quadrant(n_x, n_y, remapped_randu, randv, &phi, &cos_theta);
+ phi = M_PI_F - phi;
+ }
+ else if (randu < 0.75f) { /* third quadrant */
+ float remapped_randu = 4.0f * (randu - 0.5f);
+ bsdf_ashikhmin_shirley_sample_first_quadrant(n_x, n_y, remapped_randu, randv, &phi, &cos_theta);
+ phi = M_PI_F + phi;
+ }
+ else { /* fourth quadrant */
+ float remapped_randu = 4.0f * (1.0f - randu);
+ bsdf_ashikhmin_shirley_sample_first_quadrant(n_x, n_y, remapped_randu, randv, &phi, &cos_theta);
+ phi = 2.0f * M_PI_F - phi;
+ }
+ }
+
+ /* get half vector in tangent space */
+ float sin_theta = sqrtf(fmaxf(0.0f, 1.0f - cos_theta*cos_theta));
+ float cos_phi = cosf(phi);
+ float sin_phi = sinf(phi); /* no sqrt(1-cos^2) here b/c it causes artifacts */
+ float3 h = make_float3(
+ sin_theta * cos_phi,
+ sin_theta * sin_phi,
+ cos_theta
+ );
+
+ /* half vector to world space */
+ float3 H = h.x*X + h.y*Y + h.z*N;
+ float HdotI = dot(H, I);
+ if (HdotI < 0.0f) H = -H;
+
+ /* reflect I on H to get omega_in */
+ *omega_in = -I + (2.0f * HdotI) * H;
+
+ /* leave the rest to eval_reflect */
+ /* (could maybe optimize a few things by manual inlining, but I doubt it would make much difference) */
+ *eval = bsdf_ashikhmin_shirley_eval_reflect(sc, I, *omega_in, pdf);
+
+#ifdef __RAY_DIFFERENTIALS__
+ /* just do the reflection thing for now */
+ *domega_in_dx = (2.0f * dot(N, dIdx)) * N - dIdx;
+ *domega_in_dy = (2.0f * dot(N, dIdy)) * N - dIdy;
+#endif
+ }
+
+ return LABEL_REFLECT | LABEL_GLOSSY;
+}
+
+
+CCL_NAMESPACE_END
+
+#endif /* __BSDF_ASHIKHMIN_SHIRLEY_H__ */
diff --git a/intern/cycles/kernel/closure/bsdf_hair.h b/intern/cycles/kernel/closure/bsdf_hair.h
index 19cdb773255..e0b5454592b 100644
--- a/intern/cycles/kernel/closure/bsdf_hair.h
+++ b/intern/cycles/kernel/closure/bsdf_hair.h
@@ -63,7 +63,7 @@ ccl_device int bsdf_hair_transmission_setup(ShaderClosure *sc)
ccl_device float3 bsdf_hair_reflection_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
#ifdef __HAIR__
- float offset = sc->offset;
+ float offset = sc->data2;
float3 Tg = sc->T;
#else
float offset = 0.0f;
@@ -120,7 +120,7 @@ ccl_device float3 bsdf_hair_reflection_eval_transmit(const ShaderClosure *sc, co
ccl_device float3 bsdf_hair_transmission_eval_transmit(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
#ifdef __HAIR__
- float offset = sc->offset;
+ float offset = sc->data2;
float3 Tg = sc->T;
#else
float offset = 0.0f;
@@ -166,7 +166,7 @@ ccl_device float3 bsdf_hair_transmission_eval_transmit(const ShaderClosure *sc,
ccl_device int bsdf_hair_reflection_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
#ifdef __HAIR__
- float offset = sc->offset;
+ float offset = sc->data2;
float3 Tg = sc->T;
#else
float offset = 0.0f;
@@ -221,7 +221,7 @@ ccl_device int bsdf_hair_reflection_sample(const ShaderClosure *sc, float3 Ng, f
ccl_device int bsdf_hair_transmission_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
#ifdef __HAIR__
- float offset = sc->offset;
+ float offset = sc->data2;
float3 Tg = sc->T;
#else
float offset = 0.0f;
diff --git a/intern/cycles/kernel/closure/bsdf_microfacet.h b/intern/cycles/kernel/closure/bsdf_microfacet.h
index 1ec35e444fe..e130349bca2 100644
--- a/intern/cycles/kernel/closure/bsdf_microfacet.h
+++ b/intern/cycles/kernel/closure/bsdf_microfacet.h
@@ -35,20 +35,359 @@
CCL_NAMESPACE_BEGIN
-/* GGX */
+/* Approximate erf and erfinv implementations
+ *
+ * Adapted from code (C) Copyright John Maddock 2006.
+ * Use, modification and distribution are subject to the
+ * Boost Software License, Version 1.0. (See accompanying file
+ * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
+
+ccl_device float approx_erff_impl(float z)
+{
+ float result;
+
+ if(z < 0.5f) {
+ if(z < 1e-10f) {
+ if(z == 0) {
+ result = 0;
+ }
+ else {
+ float c = 0.0033791670f;
+ result = z * 1.125f + z * c;
+ }
+ }
+ else {
+ float Y = 1.044948577f;
+
+ float zz = z * z;
+ float num = (((-0.007727583f * zz) + -0.050999073f)*zz + -0.338165134f)*zz + 0.083430589f;
+ float denom = (((0.000370900f * zz) + 0.008585719f)*zz + 0.087522260f)*zz + 0.455004033f;
+ result = z * (Y + num / denom);
+ }
+ }
+ else if(z < 2.5f) {
+ if(z < 1.5f) {
+ float Y = 0.4059357643f;
+ float fz = z - 0.5f;
+
+ float num = (((0.088890036f * fz) + 0.191003695f)*fz + 0.178114665f)*fz + -0.098090592f;
+ float denom = (((0.123850974f * fz) + 0.578052804f)*fz + 1.426280048f)*fz + 1.847590709f;
+
+ result = Y + num / denom;
+ result *= expf(-z * z) / z;
+ }
+ else {
+ float Y = 0.506728172f;
+ float fz = z - 1.5f;
+ float num = (((0.017567943f * fz) + 0.043948189f)*fz + 0.038654037f)*fz + -0.024350047f;
+ float denom = (((0.325732924f * fz) + 0.982403709f)*fz + 1.539914949f)*fz + 1;
+
+ result = Y + num / denom;
+ result *= expf(-z * z) / z;
+ }
+
+ result = 1 - result;
+ }
+ else {
+ result = 1;
+ }
+
+ return result;
+}
+
+ccl_device float approx_erff(float z)
+{
+ float s = 1.0f;
+
+ if(z < 0.0f) {
+ s = -1.0f;
+ z = -z;
+ }
+
+ return s * approx_erff_impl(z);
+}
+
+ccl_device float approx_erfinvf_impl(float p, float q)
+{
+ float result = 0;
+
+ if(p <= 0.5f) {
+ float Y = 0.089131474f;
+ float g = p * (p + 10);
+ float num = (((-0.012692614f * p) + 0.033480662f)*p + -0.008368748f)*p + -0.000508781f;
+ float denom = (((1.562215583f * p) + -1.565745582f)*p + -0.970005043f)*p + 1.0f;
+ float r = num / denom;
+ result = g * Y + g * r;
+ }
+ else if(q >= 0.25f) {
+ float Y = 2.249481201f;
+ float g = sqrtf(-2 * logf(q));
+ float xs = q - 0.25f;
+ float num = (((17.644729840f * xs) + 8.370503283f)*xs + 0.105264680f)*xs + -0.202433508f;
+ float denom = (((-28.660818049f * xs) + 3.971343795f)*xs + 6.242641248f)*xs + 1.0f;
+ float r = num / denom;
+ result = g / (Y + r);
+ }
+ else {
+ float x = sqrtf(-logf(q));
+
+ if(x < 3) {
+ float Y = 0.807220458f;
+ float xs = x - 1.125f;
+ float num = (((0.387079738f * xs) + 0.117030156f)*xs + -0.163794047f)*xs + -0.131102781f;
+ float denom = (((4.778465929f * xs) + 5.381683457f)*xs + 3.466254072f)*xs + 1.0f;
+ float R = num / denom;
+ result = Y * x + R * x;
+ }
+ else {
+ float Y = 0.939955711f;
+ float xs = x - 3;
+ float num = (((0.009508047f * xs) + 0.018557330f)*xs + -0.002224265f)*xs + -0.035035378f;
+ float denom = (((0.220091105f * xs) + 0.762059164f)*xs + 1.365334981f)*xs + 1.0f;
+ float R = num / denom;
+ result = Y * x + R * x;
+ }
+ }
+
+ return result;
+}
+
+ccl_device float approx_erfinvf(float z)
+{
+ float p, q, s;
+
+ if(z < 0) {
+ p = -z;
+ q = 1 - p;
+ s = -1;
+ }
+ else {
+ p = z;
+ q = 1 - z;
+ s = 1;
+ }
+
+ return s * approx_erfinvf_impl(p, q);
+}
+
+/* Beckmann and GGX microfacet importance sampling from:
+ *
+ * Importance Sampling Microfacet-Based BSDFs using the Distribution of Visible Normals.
+ * E. Heitz and E. d'Eon, EGSR 2014 */
+
+ccl_device_inline void microfacet_beckmann_sample_slopes(
+ KernelGlobals *kg,
+ const float cos_theta_i, const float sin_theta_i,
+ float randu, float randv, float *slope_x, float *slope_y,
+ float *G1i)
+{
+ /* special case (normal incidence) */
+ if(cos_theta_i >= 0.99999f) {
+ const float r = sqrtf(-logf(randu));
+ const float phi = M_2PI_F * randv;
+ *slope_x = r * cosf(phi);
+ *slope_y = r * sinf(phi);
+ *G1i = 1.0f;
+ return;
+ }
+
+ /* precomputations */
+ const float tan_theta_i = sin_theta_i/cos_theta_i;
+ const float inv_a = tan_theta_i;
+ const float a = 1.0f/inv_a;
+ const float erf_a = approx_erff(a);
+ const float exp_a2 = expf(-a*a);
+ const float SQRT_PI_INV = 0.56418958354f;
+ const float Lambda = 0.5f*(erf_a - 1.0f) + (0.5f*SQRT_PI_INV)*(exp_a2*inv_a);
+ const float G1 = 1.0f/(1.0f + Lambda); /* masking */
+
+ *G1i = G1;
+
+#if 0
+ const float C = 1.0f - G1 * erf_a;
+
+ /* sample slope X */
+ if(randu < C) {
+ /* rescale randu */
+ randu = randu / C;
+ const float w_1 = 0.5f * SQRT_PI_INV * sin_theta_i * exp_a2;
+ const float w_2 = cos_theta_i * (0.5f - 0.5f*erf_a);
+ const float p = w_1 / (w_1 + w_2);
+
+ if(randu < p) {
+ randu = randu / p;
+ *slope_x = -sqrtf(-logf(randu*exp_a2));
+ }
+ else {
+ randu = (randu - p) / (1.0f - p);
+ *slope_x = approx_erfinvf(randu - 1.0f - randu*erf_a);
+ }
+ }
+ else {
+ /* rescale randu */
+ randu = (randu - C) / (1.0f - C);
+ *slope_x = approx_erfinvf((-1.0f + 2.0f*randu)*erf_a);
+
+ const float p = (-(*slope_x)*sin_theta_i + cos_theta_i) / (2.0f*cos_theta_i);
+
+ if(randv > p) {
+ *slope_x = -(*slope_x);
+ randv = (randv - p) / (1.0f - p);
+ }
+ else
+ randv = randv / p;
+ }
+
+ /* sample slope Y */
+ *slope_y = approx_erfinvf(2.0f*randv - 1.0f);
+#else
+ /* use precomputed table, because it better preserves stratification
+ * of the random number pattern */
+ int beckmann_table_offset = kernel_data.tables.beckmann_offset;
+
+ *slope_x = lookup_table_read_2D(kg, randu, cos_theta_i,
+ beckmann_table_offset, BECKMANN_TABLE_SIZE, BECKMANN_TABLE_SIZE);
+ *slope_y = approx_erfinvf(2.0f*randv - 1.0f);
+#endif
+
+}
+
+ccl_device_inline void microfacet_ggx_sample_slopes(
+ const float cos_theta_i, const float sin_theta_i,
+ float randu, float randv, float *slope_x, float *slope_y,
+ float *G1i)
+{
+ /* special case (normal incidence) */
+ if(cos_theta_i >= 0.99999f) {
+ const float r = sqrtf(randu/(1.0f - randu));
+ const float phi = M_2PI_F * randv;
+ *slope_x = r * cosf(phi);
+ *slope_y = r * sinf(phi);
+ *G1i = 1.0f;
+
+ return;
+ }
+
+ /* precomputations */
+ const float tan_theta_i = sin_theta_i/cos_theta_i;
+ const float G1_inv = 0.5f * (1.0f + safe_sqrtf(1.0f + tan_theta_i*tan_theta_i));
+
+ *G1i = 1.0f/G1_inv;
+
+ /* sample slope_x */
+ const float A = 2.0f*randu*G1_inv - 1.0f;
+ const float AA = A*A;
+ const float tmp = 1.0f/(AA - 1.0f);
+ const float B = tan_theta_i;
+ const float BB = B*B;
+ const float D = safe_sqrtf(BB*(tmp*tmp) - (AA - BB)*tmp);
+ const float slope_x_1 = B*tmp - D;
+ const float slope_x_2 = B*tmp + D;
+ *slope_x = (A < 0.0f || slope_x_2*tan_theta_i > 1.0f)? slope_x_1: slope_x_2;
+
+ /* sample slope_y */
+ float S;
+
+ if(randv > 0.5f) {
+ S = 1.0f;
+ randv = 2.0f*(randv - 0.5f);
+ }
+ else {
+ S = -1.0f;
+ randv = 2.0f*(0.5f - randv);
+ }
+
+ const float z = (randv*(randv*(randv*0.27385f - 0.73369f) + 0.46341f)) / (randv*(randv*(randv*0.093073f + 0.309420f) - 1.000000f) + 0.597999f);
+ *slope_y = S * z * safe_sqrtf(1.0f + (*slope_x)*(*slope_x));
+}
+
+ccl_device_inline float3 microfacet_sample_stretched(
+ KernelGlobals *kg, const float3 omega_i,
+ const float alpha_x, const float alpha_y,
+ const float randu, const float randv,
+ bool beckmann, float *G1i)
+{
+ /* 1. stretch omega_i */
+ float3 omega_i_ = make_float3(alpha_x * omega_i.x, alpha_y * omega_i.y, omega_i.z);
+ omega_i_ = normalize(omega_i_);
+
+ /* get polar coordinates of omega_i_ */
+ float costheta_ = 1.0f;
+ float sintheta_ = 0.0f;
+ float cosphi_ = 1.0f;
+ float sinphi_ = 0.0f;
+
+ if(omega_i_.z < 0.99999f) {
+ costheta_ = omega_i_.z;
+ sintheta_ = safe_sqrtf(1.0f - costheta_*costheta_);
+
+ float invlen = 1.0f/sintheta_;
+ cosphi_ = omega_i_.x * invlen;
+ sinphi_ = omega_i_.y * invlen;
+ }
+
+ /* 2. sample P22_{omega_i}(x_slope, y_slope, 1, 1) */
+ float slope_x, slope_y;
+
+ if(beckmann) {
+ microfacet_beckmann_sample_slopes(kg, costheta_, sintheta_,
+ randu, randv, &slope_x, &slope_y, G1i);
+ }
+ else {
+ microfacet_ggx_sample_slopes(costheta_, sintheta_,
+ randu, randv, &slope_x, &slope_y, G1i);
+ }
+
+ /* 3. rotate */
+ float tmp = cosphi_*slope_x - sinphi_*slope_y;
+ slope_y = sinphi_*slope_x + cosphi_*slope_y;
+ slope_x = tmp;
+
+ /* 4. unstretch */
+ slope_x = alpha_x * slope_x;
+ slope_y = alpha_y * slope_y;
+
+ /* 5. compute normal */
+ return normalize(make_float3(-slope_x, -slope_y, 1.0f));
+}
+
+/* GGX microfacet with Smith shadow-masking from:
+ *
+ * Microfacet Models for Refraction through Rough Surfaces
+ * B. Walter, S. R. Marschner, H. Li, K. E. Torrance, EGSR 2007
+ *
+ * Anisotropic from:
+ *
+ * Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs
+ * E. Heitz, Research Report 2014
+ *
+ * Anisotropy is only supported for reflection currently, but adding it for
+ * tranmission is just a matter of copying code from reflection if needed. */
ccl_device int bsdf_microfacet_ggx_setup(ShaderClosure *sc)
{
- sc->data0 = clamp(sc->data0, 0.0f, 1.0f); /* m_ag */
+ sc->data0 = clamp(sc->data0, 0.0f, 1.0f); /* alpha_x */
+ sc->data1 = sc->data0; /* alpha_y */
sc->type = CLOSURE_BSDF_MICROFACET_GGX_ID;
return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_GLOSSY;
}
+ccl_device int bsdf_microfacet_ggx_aniso_setup(ShaderClosure *sc)
+{
+ sc->data0 = clamp(sc->data0, 0.0f, 1.0f); /* alpha_x */
+ sc->data1 = clamp(sc->data1, 0.0f, 1.0f); /* alpha_y */
+
+ sc->type = CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID;
+
+ return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_GLOSSY;
+}
+
ccl_device int bsdf_microfacet_ggx_refraction_setup(ShaderClosure *sc)
{
- sc->data0 = clamp(sc->data0, 0.0f, 1.0f); /* m_ag */
+ sc->data0 = clamp(sc->data0, 0.0f, 1.0f); /* alpha_x */
+ sc->data1 = sc->data1; /* alpha_y */
sc->type = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
@@ -57,136 +396,246 @@ ccl_device int bsdf_microfacet_ggx_refraction_setup(ShaderClosure *sc)
ccl_device void bsdf_microfacet_ggx_blur(ShaderClosure *sc, float roughness)
{
- sc->data0 = fmaxf(roughness, sc->data0); /* m_ag */
+ sc->data0 = fmaxf(roughness, sc->data0); /* alpha_x */
+ sc->data1 = fmaxf(roughness, sc->data1); /* alpha_y */
}
ccl_device float3 bsdf_microfacet_ggx_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float m_ag = max(sc->data0, 1e-4f);
+ float alpha_x = sc->data0;
+ float alpha_y = sc->data1;
int m_refractive = sc->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
float3 N = sc->N;
- if(m_refractive || m_ag <= 1e-4f)
- return make_float3 (0, 0, 0);
+ if(m_refractive || fmaxf(alpha_x, alpha_y) <= 1e-4f)
+ return make_float3(0, 0, 0);
+
float cosNO = dot(N, I);
float cosNI = dot(N, omega_in);
+
if(cosNI > 0 && cosNO > 0) {
- // get half vector
- float3 Hr = normalize(omega_in + I);
- // eq. 20: (F*G*D)/(4*in*on)
- // eq. 33: first we calculate D(m) with m=Hr:
- float alpha2 = m_ag * m_ag;
- float cosThetaM = dot(N, Hr);
- float cosThetaM2 = cosThetaM * cosThetaM;
- float tanThetaM2 = (1 - cosThetaM2) / cosThetaM2;
- float cosThetaM4 = cosThetaM2 * cosThetaM2;
- float D = alpha2 / (M_PI_F * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2));
- // eq. 34: now calculate G1(i,m) and G1(o,m)
- float G1o = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNO * cosNO) / (cosNO * cosNO)));
- float G1i = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNI * cosNI) / (cosNI * cosNI)));
+ /* get half vector */
+ float3 m = normalize(omega_in + I);
+ float alpha2 = alpha_x * alpha_y;
+ float D, G1o, G1i;
+
+ if(alpha_x == alpha_y) {
+ /* isotropic
+ * eq. 20: (F*G*D)/(4*in*on)
+ * eq. 33: first we calculate D(m) */
+ float cosThetaM = dot(N, m);
+ float cosThetaM2 = cosThetaM * cosThetaM;
+ float cosThetaM4 = cosThetaM2 * cosThetaM2;
+ float tanThetaM2 = (1 - cosThetaM2) / cosThetaM2;
+ D = alpha2 / (M_PI_F * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2));
+
+ /* eq. 34: now calculate G1(i,m) and G1(o,m) */
+ G1o = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNO * cosNO) / (cosNO * cosNO)));
+ G1i = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNI * cosNI) / (cosNI * cosNI)));
+ }
+ else {
+ /* anisotropic */
+ float3 X, Y, Z = N;
+ make_orthonormals_tangent(Z, sc->T, &X, &Y);
+
+ /* distribution */
+ float3 local_m = make_float3(dot(X, m), dot(Y, m), dot(Z, m));
+ float slope_x = -local_m.x/(local_m.z*alpha_x);
+ float slope_y = -local_m.y/(local_m.z*alpha_y);
+ float slope_len = 1 + slope_x*slope_x + slope_y*slope_y;
+
+ float cosThetaM = local_m.z;
+ float cosThetaM2 = cosThetaM * cosThetaM;
+ float cosThetaM4 = cosThetaM2 * cosThetaM2;
+
+ D = 1 / ((slope_len * slope_len) * M_PI_F * alpha2 * cosThetaM4);
+
+ /* G1(i,m) and G1(o,m) */
+ float tanThetaO2 = (1 - cosNO * cosNO) / (cosNO * cosNO);
+ float cosPhiO = dot(I, X);
+ float sinPhiO = dot(I, Y);
+
+ float alphaO2 = (cosPhiO*cosPhiO)*(alpha_x*alpha_x) + (sinPhiO*sinPhiO)*(alpha_y*alpha_y);
+ alphaO2 /= cosPhiO*cosPhiO + sinPhiO*sinPhiO;
+
+ G1o = 2 / (1 + safe_sqrtf(1 + alphaO2 * tanThetaO2));
+
+ float tanThetaI2 = (1 - cosNI * cosNI) / (cosNI * cosNI);
+ float cosPhiI = dot(omega_in, X);
+ float sinPhiI = dot(omega_in, Y);
+
+ float alphaI2 = (cosPhiI*cosPhiI)*(alpha_x*alpha_x) + (sinPhiI*sinPhiI)*(alpha_y*alpha_y);
+ alphaI2 /= cosPhiI*cosPhiI + sinPhiI*sinPhiI;
+
+ G1i = 2 / (1 + safe_sqrtf(1 + alphaI2 * tanThetaI2));
+ }
+
float G = G1o * G1i;
- float out = (G * D) * 0.25f / cosNO;
- // eq. 24
- float pm = D * cosThetaM;
- // convert into pdf of the sampled direction
- // eq. 38 - but see also:
- // eq. 17 in http://www.graphics.cornell.edu/~bjw/wardnotes.pdf
- *pdf = pm * 0.25f / dot(Hr, I);
- return make_float3 (out, out, out);
+
+ /* eq. 20 */
+ float common = D * 0.25f / cosNO;
+ float out = G * common;
+
+ /* eq. 2 in distribution of visible normals sampling
+ * pm = Dw = G1o * dot(m, I) * D / dot(N, I); */
+
+ /* eq. 38 - but see also:
+ * eq. 17 in http://www.graphics.cornell.edu/~bjw/wardnotes.pdf
+ * pdf = pm * 0.25 / dot(m, I); */
+ *pdf = G1o * common;
+
+ return make_float3(out, out, out);
}
- return make_float3 (0, 0, 0);
+
+ return make_float3(0, 0, 0);
}
ccl_device float3 bsdf_microfacet_ggx_eval_transmit(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float m_ag = max(sc->data0, 1e-4f);
- float m_eta = sc->data1;
+ float alpha_x = sc->data0;
+ float alpha_y = sc->data1;
+ float m_eta = sc->data2;
int m_refractive = sc->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
float3 N = sc->N;
- if(!m_refractive || m_ag <= 1e-4f)
- return make_float3 (0, 0, 0);
+ if(!m_refractive || fmaxf(alpha_x, alpha_y) <= 1e-4f)
+ return make_float3(0, 0, 0);
+
float cosNO = dot(N, I);
float cosNI = dot(N, omega_in);
+
if(cosNO <= 0 || cosNI >= 0)
- return make_float3 (0, 0, 0); // vectors on same side -- not possible
- // compute half-vector of the refraction (eq. 16)
+ return make_float3(0, 0, 0); /* vectors on same side -- not possible */
+
+ /* compute half-vector of the refraction (eq. 16) */
float3 ht = -(m_eta * omega_in + I);
float3 Ht = normalize(ht);
float cosHO = dot(Ht, I);
-
float cosHI = dot(Ht, omega_in);
- // eq. 33: first we calculate D(m) with m=Ht:
- float alpha2 = m_ag * m_ag;
+
+ float D, G1o, G1i;
+
+ /* eq. 33: first we calculate D(m) with m=Ht: */
+ float alpha2 = alpha_x * alpha_y;
float cosThetaM = dot(N, Ht);
float cosThetaM2 = cosThetaM * cosThetaM;
float tanThetaM2 = (1 - cosThetaM2) / cosThetaM2;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
- float D = alpha2 / (M_PI_F * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2));
- // eq. 34: now calculate G1(i,m) and G1(o,m)
- float G1o = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNO * cosNO) / (cosNO * cosNO)));
- float G1i = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNI * cosNI) / (cosNI * cosNI)));
+ D = alpha2 / (M_PI_F * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2));
+
+ /* eq. 34: now calculate G1(i,m) and G1(o,m) */
+ G1o = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNO * cosNO) / (cosNO * cosNO)));
+ G1i = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNI * cosNI) / (cosNI * cosNI)));
+
float G = G1o * G1i;
- // probability
- float invHt2 = 1 / dot(ht, ht);
- *pdf = D * fabsf(cosThetaM) * (fabsf(cosHI) * (m_eta * m_eta)) * invHt2;
- float out = (fabsf(cosHI * cosHO) * (m_eta * m_eta) * (G * D) * invHt2) / cosNO;
- return make_float3 (out, out, out);
+
+ /* probability */
+ float Ht2 = dot(ht, ht);
+
+ /* eq. 2 in distribution of visible normals sampling
+ * pm = Dw = G1o * dot(m, I) * D / dot(N, I); */
+
+ /* out = fabsf(cosHI * cosHO) * (m_eta * m_eta) * G * D / (cosNO * Ht2)
+ * pdf = pm * (m_eta * m_eta) * fabsf(cosHI) / Ht2 */
+ float common = D * (m_eta * m_eta) / (cosNO * Ht2);
+ float out = G * fabsf(cosHI * cosHO) * common;
+ *pdf = G1o * cosHO * fabsf(cosHI) * common;
+
+ return make_float3(out, out, out);
}
-ccl_device int bsdf_microfacet_ggx_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
+ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals *kg, const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float m_ag = sc->data0;
+ float alpha_x = sc->data0;
+ float alpha_y = sc->data1;
int m_refractive = sc->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
float3 N = sc->N;
float cosNO = dot(N, I);
if(cosNO > 0) {
float3 X, Y, Z = N;
- make_orthonormals(Z, &X, &Y);
- // generate a random microfacet normal m
- // eq. 35,36:
- // we take advantage of cos(atan(x)) == 1/sqrt(1+x^2)
- //tttt and sin(atan(x)) == x/sqrt(1+x^2)
- float alpha2 = m_ag * m_ag;
- float tanThetaM2 = alpha2 * randu / (1 - randu);
- float cosThetaM = 1 / safe_sqrtf(1 + tanThetaM2);
- float sinThetaM = cosThetaM * safe_sqrtf(tanThetaM2);
- float phiM = M_2PI_F * randv;
- float3 m = (cosf(phiM) * sinThetaM) * X +
- (sinf(phiM) * sinThetaM) * Y +
- ( cosThetaM) * Z;
+
+ if(alpha_x == alpha_y)
+ make_orthonormals(Z, &X, &Y);
+ else
+ make_orthonormals_tangent(Z, sc->T, &X, &Y);
+
+ /* importance sampling with distribution of visible normals. vectors are
+ * transformed to local space before and after */
+ float3 local_I = make_float3(dot(X, I), dot(Y, I), cosNO);
+ float3 local_m;
+ float G1o;
+
+ local_m = microfacet_sample_stretched(kg, local_I, alpha_x, alpha_y,
+ randu, randv, false, &G1o);
+
+ float3 m = X*local_m.x + Y*local_m.y + Z*local_m.z;
+ float cosThetaM = local_m.z;
+
+ /* reflection or refraction? */
if(!m_refractive) {
float cosMO = dot(m, I);
+
if(cosMO > 0) {
- // eq. 39 - compute actual reflected direction
+ /* eq. 39 - compute actual reflected direction */
*omega_in = 2 * cosMO * m - I;
+
if(dot(Ng, *omega_in) > 0) {
- if (m_ag <= 1e-4f) {
- // some high number for MIS
+ if(fmaxf(alpha_x, alpha_y) <= 1e-4f) {
+ /* some high number for MIS */
*pdf = 1e6f;
*eval = make_float3(1e6f, 1e6f, 1e6f);
}
else {
- // microfacet normal is visible to this ray
- // eq. 33
- float cosThetaM2 = cosThetaM * cosThetaM;
- float cosThetaM4 = cosThetaM2 * cosThetaM2;
- float D = alpha2 / (M_PI_F * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2));
- // eq. 24
- float pm = D * cosThetaM;
- // convert into pdf of the sampled direction
- // eq. 38 - but see also:
- // eq. 17 in http://www.graphics.cornell.edu/~bjw/wardnotes.pdf
- *pdf = pm * 0.25f / cosMO;
- // eval BRDF*cosNI
- float cosNI = dot(N, *omega_in);
- // eq. 34: now calculate G1(i,m) and G1(o,m)
- float G1o = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNO * cosNO) / (cosNO * cosNO)));
- float G1i = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNI * cosNI) / (cosNI * cosNI)));
- float G = G1o * G1i;
- // eq. 20: (F*G*D)/(4*in*on)
- float out = (G * D) * 0.25f / cosNO;
+ /* microfacet normal is visible to this ray */
+ /* eq. 33 */
+ float alpha2 = alpha_x * alpha_y;
+ float D, G1i;
+
+ if(alpha_x == alpha_y) {
+ /* isotropic */
+ float cosThetaM2 = cosThetaM * cosThetaM;
+ float cosThetaM4 = cosThetaM2 * cosThetaM2;
+ float tanThetaM2 = 1/(cosThetaM2) - 1;
+ D = alpha2 / (M_PI_F * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2));
+
+ /* eval BRDF*cosNI */
+ float cosNI = dot(N, *omega_in);
+
+ /* eq. 34: now calculate G1(i,m) */
+ G1i = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNI * cosNI) / (cosNI * cosNI)));
+ }
+ else {
+ /* anisotropic distribution */
+ float3 local_m = make_float3(dot(X, m), dot(Y, m), dot(Z, m));
+ float slope_x = -local_m.x/(local_m.z*alpha_x);
+ float slope_y = -local_m.y/(local_m.z*alpha_y);
+ float slope_len = 1 + slope_x*slope_x + slope_y*slope_y;
+
+ float cosThetaM = local_m.z;
+ float cosThetaM2 = cosThetaM * cosThetaM;
+ float cosThetaM4 = cosThetaM2 * cosThetaM2;
+
+ D = 1 / ((slope_len * slope_len) * M_PI_F * alpha2 * cosThetaM4);
+
+ /* calculate G1(i,m) */
+ float cosNI = dot(N, *omega_in);
+
+ float tanThetaI2 = (1 - cosNI * cosNI) / (cosNI * cosNI);
+ float cosPhiI = dot(*omega_in, X);
+ float sinPhiI = dot(*omega_in, Y);
+
+ float alphaI2 = (cosPhiI*cosPhiI)*(alpha_x*alpha_x) + (sinPhiI*sinPhiI)*(alpha_y*alpha_y);
+ alphaI2 /= cosPhiI*cosPhiI + sinPhiI*sinPhiI;
+
+ G1i = 2 / (1 + safe_sqrtf(1 + alphaI2 * tanThetaI2));
+ }
+
+ /* see eval function for derivation */
+ float common = (G1o * D) * 0.25f / cosNO;
+ float out = G1i * common;
+ *pdf = common;
+
*eval = make_float3(out, out, out);
}
@@ -198,14 +647,15 @@ ccl_device int bsdf_microfacet_ggx_sample(const ShaderClosure *sc, float3 Ng, fl
}
}
else {
- // CAUTION: the i and o variables are inverted relative to the paper
- // eq. 39 - compute actual refractive direction
+ /* CAUTION: the i and o variables are inverted relative to the paper
+ * eq. 39 - compute actual refractive direction */
float3 R, T;
#ifdef __RAY_DIFFERENTIALS__
float3 dRdx, dRdy, dTdx, dTdy;
#endif
- float m_eta = sc->data1;
+ float m_eta = sc->data2;
bool inside;
+
fresnel_dielectric(m_eta, m, I, &R, &T,
#ifdef __RAY_DIFFERENTIALS__
dIdx, dIdy, &dRdx, &dRdy, &dTdx, &dTdy,
@@ -213,38 +663,43 @@ ccl_device int bsdf_microfacet_ggx_sample(const ShaderClosure *sc, float3 Ng, fl
&inside);
if(!inside) {
+
*omega_in = T;
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = dTdx;
*domega_in_dy = dTdy;
#endif
- if (m_ag <= 1e-4f || fabsf(m_eta - 1.0f) < 1e-4f) {
- // some high number for MIS
+ if(fmaxf(alpha_x, alpha_y) <= 1e-4f || fabsf(m_eta - 1.0f) < 1e-4f) {
+ /* some high number for MIS */
*pdf = 1e6f;
*eval = make_float3(1e6f, 1e6f, 1e6f);
}
else {
- // eq. 33
+ /* eq. 33 */
+ float alpha2 = alpha_x * alpha_y;
float cosThetaM2 = cosThetaM * cosThetaM;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
+ float tanThetaM2 = 1/(cosThetaM2) - 1;
float D = alpha2 / (M_PI_F * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2));
- // eq. 24
- float pm = D * cosThetaM;
- // eval BRDF*cosNI
+
+ /* eval BRDF*cosNI */
float cosNI = dot(N, *omega_in);
- // eq. 34: now calculate G1(i,m) and G1(o,m)
- float G1o = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNO * cosNO) / (cosNO * cosNO)));
+
+ /* eq. 34: now calculate G1(i,m) */
float G1i = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNI * cosNI) / (cosNI * cosNI)));
- float G = G1o * G1i;
- // eq. 21
+
+ /* eq. 21 */
float cosHI = dot(m, *omega_in);
float cosHO = dot(m, I);
float Ht2 = m_eta * cosHI + cosHO;
Ht2 *= Ht2;
- float out = (fabsf(cosHI * cosHO) * (m_eta * m_eta) * (G * D)) / (cosNO * Ht2);
- // eq. 38 and eq. 17
- *pdf = pm * (m_eta * m_eta) * fabsf(cosHI) / Ht2;
+
+ /* see eval function for derivation */
+ float common = (G1o * D) * (m_eta * m_eta) / (cosNO * Ht2);
+ float out = G1i * fabsf(cosHI * cosHO) * common;
+ *pdf = cosHO * fabsf(cosHI) * common;
+
*eval = make_float3(out, out, out);
}
}
@@ -253,19 +708,33 @@ ccl_device int bsdf_microfacet_ggx_sample(const ShaderClosure *sc, float3 Ng, fl
return (m_refractive) ? LABEL_TRANSMIT|LABEL_GLOSSY : LABEL_REFLECT|LABEL_GLOSSY;
}
-/* BECKMANN */
+/* Beckmann microfacet with Smith shadow-masking from:
+ *
+ * Microfacet Models for Refraction through Rough Surfaces
+ * B. Walter, S. R. Marschner, H. Li, K. E. Torrance, EGSR 2007 */
ccl_device int bsdf_microfacet_beckmann_setup(ShaderClosure *sc)
{
- sc->data0 = clamp(sc->data0, 0.0f, 1.0f); /* m_ab */
+ sc->data0 = clamp(sc->data0, 0.0f, 1.0f); /* alpha_x */
+ sc->data1 = sc->data0; /* alpha_y */
sc->type = CLOSURE_BSDF_MICROFACET_BECKMANN_ID;
return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_GLOSSY;
}
+ccl_device int bsdf_microfacet_beckmann_aniso_setup(ShaderClosure *sc)
+{
+ sc->data0 = clamp(sc->data0, 0.0f, 1.0f); /* alpha_x */
+ sc->data1 = clamp(sc->data1, 0.0f, 1.0f); /* alpha_y */
+
+ sc->type = CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID;
+ return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_GLOSSY;
+}
+
ccl_device int bsdf_microfacet_beckmann_refraction_setup(ShaderClosure *sc)
{
- sc->data0 = clamp(sc->data0, 0.0f, 1.0f); /* m_ab */
+ sc->data0 = clamp(sc->data0, 0.0f, 1.0f); /* alpha_x */
+ sc->data1 = sc->data1; /* alpha_y */
sc->type = CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_GLOSSY;
@@ -273,155 +742,253 @@ ccl_device int bsdf_microfacet_beckmann_refraction_setup(ShaderClosure *sc)
ccl_device void bsdf_microfacet_beckmann_blur(ShaderClosure *sc, float roughness)
{
- sc->data0 = fmaxf(roughness, sc->data0); /* m_ab */
+ sc->data0 = fmaxf(roughness, sc->data0); /* alpha_x */
+ sc->data1 = fmaxf(roughness, sc->data1); /* alpha_y */
}
ccl_device float3 bsdf_microfacet_beckmann_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float m_ab = max(sc->data0, 1e-4f);
+ float alpha_x = sc->data0;
+ float alpha_y = sc->data1;
int m_refractive = sc->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
float3 N = sc->N;
- if(m_refractive || m_ab <= 1e-4f)
- return make_float3 (0, 0, 0);
+ if(m_refractive || fmaxf(alpha_x, alpha_y) <= 1e-4f)
+ return make_float3(0, 0, 0);
+
float cosNO = dot(N, I);
float cosNI = dot(N, omega_in);
+
if(cosNO > 0 && cosNI > 0) {
- // get half vector
- float3 Hr = normalize(omega_in + I);
- // eq. 20: (F*G*D)/(4*in*on)
- // eq. 25: first we calculate D(m) with m=Hr:
- float alpha2 = m_ab * m_ab;
- float cosThetaM = dot(N, Hr);
- float cosThetaM2 = cosThetaM * cosThetaM;
- float tanThetaM2 = (1 - cosThetaM2) / cosThetaM2;
- float cosThetaM4 = cosThetaM2 * cosThetaM2;
- float D = expf(-tanThetaM2 / alpha2) / (M_PI_F * alpha2 * cosThetaM4);
- // eq. 26, 27: now calculate G1(i,m) and G1(o,m)
- float ao = 1 / (m_ab * safe_sqrtf((1 - cosNO * cosNO) / (cosNO * cosNO)));
- float ai = 1 / (m_ab * safe_sqrtf((1 - cosNI * cosNI) / (cosNI * cosNI)));
- float G1o = ao < 1.6f ? (3.535f * ao + 2.181f * ao * ao) / (1 + 2.276f * ao + 2.577f * ao * ao) : 1.0f;
- float G1i = ai < 1.6f ? (3.535f * ai + 2.181f * ai * ai) / (1 + 2.276f * ai + 2.577f * ai * ai) : 1.0f;
- float G = G1o * G1i;
- float out = (G * D) * 0.25f / cosNO;
- // eq. 24
- float pm = D * cosThetaM;
- // convert into pdf of the sampled direction
- // eq. 38 - but see also:
- // eq. 17 in http://www.graphics.cornell.edu/~bjw/wardnotes.pdf
- *pdf = pm * 0.25f / dot(Hr, I);
- return make_float3 (out, out, out);
+ /* get half vector */
+ float3 m = normalize(omega_in + I);
+
+ float alpha2 = alpha_x * alpha_y;
+ float D, G1o, G1i;
+
+ if(alpha_x == alpha_y) {
+ /* isotropic
+ * eq. 20: (F*G*D)/(4*in*on)
+ * eq. 25: first we calculate D(m) */
+ float cosThetaM = dot(N, m);
+ float cosThetaM2 = cosThetaM * cosThetaM;
+ float tanThetaM2 = (1 - cosThetaM2) / cosThetaM2;
+ float cosThetaM4 = cosThetaM2 * cosThetaM2;
+ D = expf(-tanThetaM2 / alpha2) / (M_PI_F * alpha2 * cosThetaM4);
+
+ /* eq. 26, 27: now calculate G1(i,m) and G1(o,m) */
+ float ao = 1 / (alpha_x * safe_sqrtf((1 - cosNO * cosNO) / (cosNO * cosNO)));
+ float ai = 1 / (alpha_x * safe_sqrtf((1 - cosNI * cosNI) / (cosNI * cosNI)));
+ G1o = ao < 1.6f ? (3.535f * ao + 2.181f * ao * ao) / (1 + 2.276f * ao + 2.577f * ao * ao) : 1.0f;
+ G1i = ai < 1.6f ? (3.535f * ai + 2.181f * ai * ai) / (1 + 2.276f * ai + 2.577f * ai * ai) : 1.0f;
+ }
+ else {
+ /* anisotropic */
+ float3 X, Y, Z = N;
+ make_orthonormals_tangent(Z, sc->T, &X, &Y);
+
+ /* distribution */
+ float3 local_m = make_float3(dot(X, m), dot(Y, m), dot(Z, m));
+ float slope_x = -local_m.x/(local_m.z*alpha_x);
+ float slope_y = -local_m.y/(local_m.z*alpha_y);
+
+ float cosThetaM = local_m.z;
+ float cosThetaM2 = cosThetaM * cosThetaM;
+ float cosThetaM4 = cosThetaM2 * cosThetaM2;
+
+ D = expf(-slope_x*slope_x - slope_y*slope_y) / (M_PI_F * alpha2 * cosThetaM4);
+
+ /* G1(i,m) and G1(o,m) */
+ float tanThetaO2 = (1 - cosNO * cosNO) / (cosNO * cosNO);
+ float cosPhiO = dot(I, X);
+ float sinPhiO = dot(I, Y);
+
+ float alphaO2 = (cosPhiO*cosPhiO)*(alpha_x*alpha_x) + (sinPhiO*sinPhiO)*(alpha_y*alpha_y);
+ alphaO2 /= cosPhiO*cosPhiO + sinPhiO*sinPhiO;
+
+ float tanThetaI2 = (1 - cosNI * cosNI) / (cosNI * cosNI);
+ float cosPhiI = dot(omega_in, X);
+ float sinPhiI = dot(omega_in, Y);
+
+ float alphaI2 = (cosPhiI*cosPhiI)*(alpha_x*alpha_x) + (sinPhiI*sinPhiI)*(alpha_y*alpha_y);
+ alphaI2 /= cosPhiI*cosPhiI + sinPhiI*sinPhiI;
+
+ float ao = 1 / (safe_sqrtf(alphaO2 * tanThetaO2));
+ float ai = 1 / (safe_sqrtf(alphaI2 * tanThetaI2));
+ G1o = ao < 1.6f ? (3.535f * ao + 2.181f * ao * ao) / (1 + 2.276f * ao + 2.577f * ao * ao) : 1.0f;
+ G1i = ai < 1.6f ? (3.535f * ai + 2.181f * ai * ai) / (1 + 2.276f * ai + 2.577f * ai * ai) : 1.0f;
+ }
+
+ float G = G1o * G1i;
+
+ /* eq. 20 */
+ float common = D * 0.25f / cosNO;
+ float out = G * common;
+
+ /* eq. 2 in distribution of visible normals sampling
+ * pm = Dw = G1o * dot(m, I) * D / dot(N, I); */
+
+ /* eq. 38 - but see also:
+ * eq. 17 in http://www.graphics.cornell.edu/~bjw/wardnotes.pdf
+ * pdf = pm * 0.25 / dot(m, I); */
+ *pdf = G1o * common;
+
+ return make_float3(out, out, out);
}
- return make_float3 (0, 0, 0);
+
+ return make_float3(0, 0, 0);
}
ccl_device float3 bsdf_microfacet_beckmann_eval_transmit(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float m_ab = max(sc->data0, 1e-4f);
- float m_eta = sc->data1;
+ float alpha_x = sc->data0;
+ float alpha_y = sc->data1;
+ float m_eta = sc->data2;
int m_refractive = sc->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
float3 N = sc->N;
- if(!m_refractive || m_ab <= 1e-4f)
- return make_float3 (0, 0, 0);
+ if(!m_refractive || fmaxf(alpha_x, alpha_y) <= 1e-4f)
+ return make_float3(0, 0, 0);
+
float cosNO = dot(N, I);
float cosNI = dot(N, omega_in);
+
if(cosNO <= 0 || cosNI >= 0)
- return make_float3 (0, 0, 0);
- // compute half-vector of the refraction (eq. 16)
+ return make_float3(0, 0, 0);
+
+ /* compute half-vector of the refraction (eq. 16) */
float3 ht = -(m_eta * omega_in + I);
float3 Ht = normalize(ht);
float cosHO = dot(Ht, I);
-
float cosHI = dot(Ht, omega_in);
- // eq. 33: first we calculate D(m) with m=Ht:
- float alpha2 = m_ab * m_ab;
+
+ /* eq. 33: first we calculate D(m) with m=Ht: */
+ float alpha2 = alpha_x * alpha_y;
float cosThetaM = min(dot(N, Ht), 1.0f);
float cosThetaM2 = cosThetaM * cosThetaM;
float tanThetaM2 = (1 - cosThetaM2) / cosThetaM2;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
float D = expf(-tanThetaM2 / alpha2) / (M_PI_F * alpha2 * cosThetaM4);
- // eq. 26, 27: now calculate G1(i,m) and G1(o,m)
- float ao = 1 / (m_ab * safe_sqrtf((1 - cosNO * cosNO) / (cosNO * cosNO)));
- float ai = 1 / (m_ab * safe_sqrtf((1 - cosNI * cosNI) / (cosNI * cosNI)));
+
+ /* eq. 26, 27: now calculate G1(i,m) and G1(o,m) */
+ float ao = 1 / (alpha_x * safe_sqrtf((1 - cosNO * cosNO) / (cosNO * cosNO)));
+ float ai = 1 / (alpha_x * safe_sqrtf((1 - cosNI * cosNI) / (cosNI * cosNI)));
float G1o = ao < 1.6f ? (3.535f * ao + 2.181f * ao * ao) / (1 + 2.276f * ao + 2.577f * ao * ao) : 1.0f;
float G1i = ai < 1.6f ? (3.535f * ai + 2.181f * ai * ai) / (1 + 2.276f * ai + 2.577f * ai * ai) : 1.0f;
float G = G1o * G1i;
- // probability
- float invHt2 = 1 / dot(ht, ht);
- *pdf = D * fabsf(cosThetaM) * (fabsf(cosHI) * (m_eta * m_eta)) * invHt2;
- float out = (fabsf(cosHI * cosHO) * (m_eta * m_eta) * (G * D) * invHt2) / cosNO;
- return make_float3 (out, out, out);
+
+ /* probability */
+ float Ht2 = dot(ht, ht);
+
+ /* eq. 2 in distribution of visible normals sampling
+ * pm = Dw = G1o * dot(m, I) * D / dot(N, I); */
+
+ /* out = fabsf(cosHI * cosHO) * (m_eta * m_eta) * G * D / (cosNO * Ht2)
+ * pdf = pm * (m_eta * m_eta) * fabsf(cosHI) / Ht2 */
+ float common = D * (m_eta * m_eta) / (cosNO * Ht2);
+ float out = G * fabsf(cosHI * cosHO) * common;
+ *pdf = G1o * cosHO * fabsf(cosHI) * common;
+
+ return make_float3(out, out, out);
}
-ccl_device int bsdf_microfacet_beckmann_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
+ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals *kg, const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float m_ab = sc->data0;
+ float alpha_x = sc->data0;
+ float alpha_y = sc->data1;
int m_refractive = sc->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
float3 N = sc->N;
float cosNO = dot(N, I);
if(cosNO > 0) {
float3 X, Y, Z = N;
- make_orthonormals(Z, &X, &Y);
- // generate a random microfacet normal m
- // eq. 35,36:
- // we take advantage of cos(atan(x)) == 1/sqrt(1+x^2)
- //tttt and sin(atan(x)) == x/sqrt(1+x^2)
- float alpha2 = m_ab * m_ab;
- float tanThetaM, cosThetaM;
-
- if(alpha2 == 0.0f) {
- tanThetaM = 0.0f;
- cosThetaM = 1.0f;
- }
- else {
- tanThetaM = safe_sqrtf(-alpha2 * logf(1 - randu));
- cosThetaM = 1 / safe_sqrtf(1 + tanThetaM * tanThetaM);
- }
- float sinThetaM = cosThetaM * tanThetaM;
- float phiM = M_2PI_F * randv;
- float3 m = (cosf(phiM) * sinThetaM) * X +
- (sinf(phiM) * sinThetaM) * Y +
- ( cosThetaM) * Z;
+ if(alpha_x == alpha_y)
+ make_orthonormals(Z, &X, &Y);
+ else
+ make_orthonormals_tangent(Z, sc->T, &X, &Y);
+ /* importance sampling with distribution of visible normals. vectors are
+ * transformed to local space before and after */
+ float3 local_I = make_float3(dot(X, I), dot(Y, I), cosNO);
+ float3 local_m;
+ float G1o;
+
+ local_m = microfacet_sample_stretched(kg, local_I, alpha_x, alpha_x,
+ randu, randv, true, &G1o);
+
+ float3 m = X*local_m.x + Y*local_m.y + Z*local_m.z;
+ float cosThetaM = local_m.z;
+
+ /* reflection or refraction? */
if(!m_refractive) {
float cosMO = dot(m, I);
+
if(cosMO > 0) {
- // eq. 39 - compute actual reflected direction
+ /* eq. 39 - compute actual reflected direction */
*omega_in = 2 * cosMO * m - I;
+
if(dot(Ng, *omega_in) > 0) {
- if (m_ab <= 1e-4f) {
- // some high number for MIS
+ if(fmaxf(alpha_x, alpha_y) <= 1e-4f) {
+ /* some high number for MIS */
*pdf = 1e6f;
*eval = make_float3(1e6f, 1e6f, 1e6f);
}
else {
- // microfacet normal is visible to this ray
- // eq. 25
- float cosThetaM2 = cosThetaM * cosThetaM;
- float tanThetaM2 = tanThetaM * tanThetaM;
- float cosThetaM4 = cosThetaM2 * cosThetaM2;
- float D = expf(-tanThetaM2 / alpha2) / (M_PI_F * alpha2 * cosThetaM4);
- // eq. 24
- float pm = D * cosThetaM;
- // convert into pdf of the sampled direction
- // eq. 38 - but see also:
- // eq. 17 in http://www.graphics.cornell.edu/~bjw/wardnotes.pdf
- *pdf = pm * 0.25f / cosMO;
- // Eval BRDF*cosNI
- float cosNI = dot(N, *omega_in);
- // eq. 26, 27: now calculate G1(i,m) and G1(o,m)
- float ao = 1 / (m_ab * safe_sqrtf((1 - cosNO * cosNO) / (cosNO * cosNO)));
- float ai = 1 / (m_ab * safe_sqrtf((1 - cosNI * cosNI) / (cosNI * cosNI)));
- float G1o = ao < 1.6f ? (3.535f * ao + 2.181f * ao * ao) / (1 + 2.276f * ao + 2.577f * ao * ao) : 1.0f;
- float G1i = ai < 1.6f ? (3.535f * ai + 2.181f * ai * ai) / (1 + 2.276f * ai + 2.577f * ai * ai) : 1.0f;
+ /* microfacet normal is visible to this ray
+ * eq. 25 */
+ float alpha2 = alpha_x * alpha_y;
+ float D, G1i;
+
+ if(alpha_x == alpha_y) {
+ /* istropic distribution */
+ float cosThetaM2 = cosThetaM * cosThetaM;
+ float cosThetaM4 = cosThetaM2 * cosThetaM2;
+ float tanThetaM2 = 1/(cosThetaM2) - 1;
+ D = expf(-tanThetaM2 / alpha2) / (M_PI_F * alpha2 * cosThetaM4);
+
+ /* eval BRDF*cosNI */
+ float cosNI = dot(N, *omega_in);
+
+ /* eq. 26, 27: now calculate G1(i,m) */
+ float ai = 1 / (alpha_x * safe_sqrtf((1 - cosNI * cosNI) / (cosNI * cosNI)));
+ G1i = ai < 1.6f ? (3.535f * ai + 2.181f * ai * ai) / (1 + 2.276f * ai + 2.577f * ai * ai) : 1.0f;
+ }
+ else {
+ /* anisotropic distribution */
+ float3 local_m = make_float3(dot(X, m), dot(Y, m), dot(Z, m));
+ float slope_x = -local_m.x/(local_m.z*alpha_x);
+ float slope_y = -local_m.y/(local_m.z*alpha_y);
+
+ float cosThetaM = local_m.z;
+ float cosThetaM2 = cosThetaM * cosThetaM;
+ float cosThetaM4 = cosThetaM2 * cosThetaM2;
+
+ D = expf(-slope_x*slope_x - slope_y*slope_y) / (M_PI_F * alpha2 * cosThetaM4);
+
+ /* G1(i,m) */
+ float cosNI = dot(N, *omega_in);
+ float tanThetaI2 = (1 - cosNI * cosNI) / (cosNI * cosNI);
+ float cosPhiI = dot(*omega_in, X);
+ float sinPhiI = dot(*omega_in, Y);
+
+ float alphaI2 = (cosPhiI*cosPhiI)*(alpha_x*alpha_x) + (sinPhiI*sinPhiI)*(alpha_y*alpha_y);
+ alphaI2 /= cosPhiI*cosPhiI + sinPhiI*sinPhiI;
+
+ float ai = 1 / (safe_sqrtf(alphaI2 * tanThetaI2));
+ G1i = ai < 1.6f ? (3.535f * ai + 2.181f * ai * ai) / (1 + 2.276f * ai + 2.577f * ai * ai) : 1.0f;
+ }
+
float G = G1o * G1i;
- // eq. 20: (F*G*D)/(4*in*on)
- float out = (G * D) * 0.25f / cosNO;
+
+ /* see eval function for derivation */
+ float common = D * 0.25f / cosNO;
+ float out = G * common;
+ *pdf = G1o * common;
+
*eval = make_float3(out, out, out);
}
+
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = (2 * dot(m, dIdx)) * m - dIdx;
*domega_in_dy = (2 * dot(m, dIdy)) * m - dIdy;
@@ -430,14 +997,15 @@ ccl_device int bsdf_microfacet_beckmann_sample(const ShaderClosure *sc, float3 N
}
}
else {
- // CAUTION: the i and o variables are inverted relative to the paper
- // eq. 39 - compute actual refractive direction
+ /* CAUTION: the i and o variables are inverted relative to the paper
+ * eq. 39 - compute actual refractive direction */
float3 R, T;
#ifdef __RAY_DIFFERENTIALS__
float3 dRdx, dRdy, dTdx, dTdy;
#endif
- float m_eta = sc->data1;
+ float m_eta = sc->data2;
bool inside;
+
fresnel_dielectric(m_eta, m, I, &R, &T,
#ifdef __RAY_DIFFERENTIALS__
dIdx, dIdy, &dRdx, &dRdy, &dTdx, &dTdy,
@@ -446,39 +1014,44 @@ ccl_device int bsdf_microfacet_beckmann_sample(const ShaderClosure *sc, float3 N
if(!inside) {
*omega_in = T;
+
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = dTdx;
*domega_in_dy = dTdy;
#endif
- if (m_ab <= 1e-4f || fabsf(m_eta - 1.0f) < 1e-4f) {
- // some high number for MIS
+
+ if(fmaxf(alpha_x, alpha_y) <= 1e-4f || fabsf(m_eta - 1.0f) < 1e-4f) {
+ /* some high number for MIS */
*pdf = 1e6f;
*eval = make_float3(1e6f, 1e6f, 1e6f);
}
else {
- // eq. 33
+ /* eq. 33 */
+ float alpha2 = alpha_x * alpha_y;
float cosThetaM2 = cosThetaM * cosThetaM;
- float tanThetaM2 = tanThetaM * tanThetaM;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
+ float tanThetaM2 = 1/(cosThetaM2) - 1;
float D = expf(-tanThetaM2 / alpha2) / (M_PI_F * alpha2 * cosThetaM4);
- // eq. 24
- float pm = D * cosThetaM;
- // eval BRDF*cosNI
+
+ /* eval BRDF*cosNI */
float cosNI = dot(N, *omega_in);
- // eq. 26, 27: now calculate G1(i,m) and G1(o,m)
- float ao = 1 / (m_ab * safe_sqrtf((1 - cosNO * cosNO) / (cosNO * cosNO)));
- float ai = 1 / (m_ab * safe_sqrtf((1 - cosNI * cosNI) / (cosNI * cosNI)));
- float G1o = ao < 1.6f ? (3.535f * ao + 2.181f * ao * ao) / (1 + 2.276f * ao + 2.577f * ao * ao) : 1.0f;
+
+ /* eq. 26, 27: now calculate G1(i,m) */
+ float ai = 1 / (alpha_x * safe_sqrtf((1 - cosNI * cosNI) / (cosNI * cosNI)));
float G1i = ai < 1.6f ? (3.535f * ai + 2.181f * ai * ai) / (1 + 2.276f * ai + 2.577f * ai * ai) : 1.0f;
float G = G1o * G1i;
- // eq. 21
+
+ /* eq. 21 */
float cosHI = dot(m, *omega_in);
float cosHO = dot(m, I);
float Ht2 = m_eta * cosHI + cosHO;
Ht2 *= Ht2;
- float out = (fabsf(cosHI * cosHO) * (m_eta * m_eta) * (G * D)) / (cosNO * Ht2);
- // eq. 38 and eq. 17
- *pdf = pm * (m_eta * m_eta) * fabsf(cosHI) / Ht2;
+
+ /* see eval function for derivation */
+ float common = D * (m_eta * m_eta) / (cosNO * Ht2);
+ float out = G * fabsf(cosHI * cosHO) * common;
+ *pdf = G1o * cosHO * fabsf(cosHI) * common;
+
*eval = make_float3(out, out, out);
}
}
diff --git a/intern/cycles/kernel/closure/bsdf_ward.h b/intern/cycles/kernel/closure/bsdf_ward.h
deleted file mode 100644
index c9de615a011..00000000000
--- a/intern/cycles/kernel/closure/bsdf_ward.h
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Adapted from Open Shading Language with this license:
- *
- * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
- * All Rights Reserved.
- *
- * Modifications Copyright 2011, Blender Foundation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Sony Pictures Imageworks nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef __BSDF_WARD_H__
-#define __BSDF_WARD_H__
-
-CCL_NAMESPACE_BEGIN
-
-/* WARD */
-
-ccl_device int bsdf_ward_setup(ShaderClosure *sc)
-{
- sc->data0 = clamp(sc->data0, 1e-4f, 1.0f); /* m_ax */
- sc->data1 = clamp(sc->data1, 1e-4f, 1.0f); /* m_ay */
-
- sc->type = CLOSURE_BSDF_WARD_ID;
- return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_GLOSSY;
-}
-
-ccl_device void bsdf_ward_blur(ShaderClosure *sc, float roughness)
-{
- sc->data0 = fmaxf(roughness, sc->data0); /* m_ax */
- sc->data1 = fmaxf(roughness, sc->data1); /* m_ay */
-}
-
-ccl_device float3 bsdf_ward_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
-{
- float m_ax = sc->data0;
- float m_ay = sc->data1;
- float3 N = sc->N;
- float3 T = sc->T;
-
- float cosNO = dot(N, I);
- float cosNI = dot(N, omega_in);
-
- if(cosNI > 0.0f && cosNO > 0.0f) {
- cosNO = max(cosNO, 1e-4f);
- cosNI = max(cosNI, 1e-4f);
-
- // get half vector and get x,y basis on the surface for anisotropy
- float3 H = normalize(omega_in + I); // normalize needed for pdf
- float3 X, Y;
- make_orthonormals_tangent(N, T, &X, &Y);
- // eq. 4
- float dotx = dot(H, X) / m_ax;
- float doty = dot(H, Y) / m_ay;
- float dotn = dot(H, N);
- float exp_arg = (dotx * dotx + doty * doty) / (dotn * dotn);
- float denom = (M_4PI_F * m_ax * m_ay * sqrtf(cosNO * cosNI));
- float exp_val = expf(-exp_arg);
- float out = cosNI * exp_val / denom;
- float oh = dot(H, I);
- denom = M_4PI_F * m_ax * m_ay * oh * dotn * dotn * dotn;
- *pdf = exp_val / denom;
- return make_float3 (out, out, out);
- }
-
- return make_float3 (0, 0, 0);
-}
-
-ccl_device float3 bsdf_ward_eval_transmit(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
-{
- return make_float3(0.0f, 0.0f, 0.0f);
-}
-
-ccl_device int bsdf_ward_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
-{
- float m_ax = sc->data0;
- float m_ay = sc->data1;
- float3 N = sc->N;
- float3 T = sc->T;
-
- float cosNO = dot(N, I);
- if(cosNO > 0.0f) {
- // get x,y basis on the surface for anisotropy
- float3 X, Y;
- make_orthonormals_tangent(N, T, &X, &Y);
- // generate random angles for the half vector
- // eq. 7 (taking care around discontinuities to keep
- //ttoutput angle in the right quadrant)
- // we take advantage of cos(atan(x)) == 1/sqrt(1+x^2)
- //tttt and sin(atan(x)) == x/sqrt(1+x^2)
- float alphaRatio = m_ay / m_ax;
- float cosPhi, sinPhi;
- if(randu < 0.25f) {
- float val = 4 * randu;
- float tanPhi = alphaRatio * tanf(M_PI_2_F * val);
- cosPhi = 1 / sqrtf(1 + tanPhi * tanPhi);
- sinPhi = tanPhi * cosPhi;
- }
- else if(randu < 0.5f) {
- float val = 1 - 4 * (0.5f - randu);
- float tanPhi = alphaRatio * tanf(M_PI_2_F * val);
- // phi = M_PI_F - phi;
- cosPhi = -1 / sqrtf(1 + tanPhi * tanPhi);
- sinPhi = -tanPhi * cosPhi;
- }
- else if(randu < 0.75f) {
- float val = 4 * (randu - 0.5f);
- float tanPhi = alphaRatio * tanf(M_PI_2_F * val);
- //phi = M_PI_F + phi;
- cosPhi = -1 / sqrtf(1 + tanPhi * tanPhi);
- sinPhi = tanPhi * cosPhi;
- }
- else {
- float val = 1 - 4 * (1 - randu);
- float tanPhi = alphaRatio * tanf(M_PI_2_F * val);
- // phi = M_2PI_F - phi;
- cosPhi = 1 / sqrtf(1 + tanPhi * tanPhi);
- sinPhi = -tanPhi * cosPhi;
- }
- // eq. 6
- // we take advantage of cos(atan(x)) == 1/sqrt(1+x^2)
- //tttt and sin(atan(x)) == x/sqrt(1+x^2)
- float thetaDenom = (cosPhi * cosPhi) / (m_ax * m_ax) + (sinPhi * sinPhi) / (m_ay * m_ay);
- float tanTheta2 = -logf(1 - randv) / thetaDenom;
- float cosTheta = 1 / sqrtf(1 + tanTheta2);
- float sinTheta = cosTheta * sqrtf(tanTheta2);
-
- float3 h; // already normalized becaused expressed from spherical coordinates
- h.x = sinTheta * cosPhi;
- h.y = sinTheta * sinPhi;
- h.z = cosTheta;
- // compute terms that are easier in local space
- float dotx = h.x / m_ax;
- float doty = h.y / m_ay;
- float dotn = h.z;
- // transform to world space
- h = h.x * X + h.y * Y + h.z * N;
- // generate the final sample
- float oh = dot(h, I);
- *omega_in = 2.0f * oh * h - I;
- if(dot(Ng, *omega_in) > 0) {
- float cosNI = dot(N, *omega_in);
- if(cosNI > 0) {
- cosNO = max(cosNO, 1e-4f);
- cosNI = max(cosNI, 1e-4f);
-
- // eq. 9
- float exp_arg = (dotx * dotx + doty * doty) / (dotn * dotn);
- float denom = M_4PI_F * m_ax * m_ay * oh * dotn * dotn * dotn;
- *pdf = expf(-exp_arg) / denom;
- // compiler will reuse expressions already computed
- denom = (M_4PI_F * m_ax * m_ay * sqrtf(cosNO * cosNI));
- float power = cosNI * expf(-exp_arg) / denom;
- *eval = make_float3(power, power, power);
-#ifdef __RAY_DIFFERENTIALS__
- *domega_in_dx = (2 * dot(N, dIdx)) * N - dIdx;
- *domega_in_dy = (2 * dot(N, dIdy)) * N - dIdy;
-#endif
- }
- }
- }
- return LABEL_REFLECT|LABEL_GLOSSY;
-}
-
-CCL_NAMESPACE_END
-
-#endif /* __BSDF_WARD_H__ */
-
diff --git a/intern/cycles/kernel/geom/geom_bvh_shadow.h b/intern/cycles/kernel/geom/geom_bvh_shadow.h
index 98bf82b3b2d..1f6e4942fab 100644
--- a/intern/cycles/kernel/geom/geom_bvh_shadow.h
+++ b/intern/cycles/kernel/geom/geom_bvh_shadow.h
@@ -68,15 +68,15 @@ ccl_device bool BVH_FUNCTION_NAME
const shuffle_swap_t shuf_identity = shuffle_swap_identity();
const shuffle_swap_t shuf_swap = shuffle_swap_swap();
- const __m128 pn = _mm_castsi128_ps(_mm_set_epi32(0x80000000, 0x80000000, 0, 0));
- __m128 Psplat[3], idirsplat[3];
+ const ssef pn = cast(ssei(0, 0, 0x80000000, 0x80000000));
+ ssef Psplat[3], idirsplat[3];
shuffle_swap_t shufflexyz[3];
- Psplat[0] = _mm_set_ps1(P.x);
- Psplat[1] = _mm_set_ps1(P.y);
- Psplat[2] = _mm_set_ps1(P.z);
+ Psplat[0] = ssef(P.x);
+ Psplat[1] = ssef(P.y);
+ Psplat[2] = ssef(P.z);
- __m128 tsplat = _mm_set_ps(-isect_t, -isect_t, 0.0f, 0.0f);
+ ssef tsplat(0.0f, 0.0f, -isect_t, -isect_t);
gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
#endif
@@ -132,27 +132,27 @@ ccl_device bool BVH_FUNCTION_NAME
/* Intersect two child bounding boxes, SSE3 version adapted from Embree */
/* fetch node data */
- const __m128 *bvh_nodes = (__m128*)kg->__bvh_nodes.data + nodeAddr*BVH_NODE_SIZE;
+ const ssef *bvh_nodes = (ssef*)kg->__bvh_nodes.data + nodeAddr*BVH_NODE_SIZE;
const float4 cnodes = ((float4*)bvh_nodes)[3];
/* intersect ray against child nodes */
- const __m128 tminmaxx = _mm_mul_ps(_mm_sub_ps(shuffle_swap(bvh_nodes[0], shufflexyz[0]), Psplat[0]), idirsplat[0]);
- const __m128 tminmaxy = _mm_mul_ps(_mm_sub_ps(shuffle_swap(bvh_nodes[1], shufflexyz[1]), Psplat[1]), idirsplat[1]);
- const __m128 tminmaxz = _mm_mul_ps(_mm_sub_ps(shuffle_swap(bvh_nodes[2], shufflexyz[2]), Psplat[2]), idirsplat[2]);
+ const ssef tminmaxx = (shuffle_swap(bvh_nodes[0], shufflexyz[0]) - Psplat[0]) * idirsplat[0];
+ const ssef tminmaxy = (shuffle_swap(bvh_nodes[1], shufflexyz[1]) - Psplat[1]) * idirsplat[1];
+ const ssef tminmaxz = (shuffle_swap(bvh_nodes[2], shufflexyz[2]) - Psplat[2]) * idirsplat[2];
/* calculate { c0min, c1min, -c0max, -c1max} */
- __m128 minmax = _mm_max_ps(_mm_max_ps(tminmaxx, tminmaxy), _mm_max_ps(tminmaxz, tsplat));
- const __m128 tminmax = _mm_xor_ps(minmax, pn);
- const __m128 lrhit = _mm_cmple_ps(tminmax, shuffle<2, 3, 0, 1>(tminmax));
+ const ssef minmax = max(max(tminmaxx, tminmaxy), max(tminmaxz, tsplat));
+ const ssef tminmax = minmax ^ pn;
+ const sseb lrhit = tminmax <= shuffle<2, 3, 0, 1>(tminmax);
/* decide which nodes to traverse next */
#ifdef __VISIBILITY_FLAG__
/* this visibility test gives a 5% performance hit, how to solve? */
- traverseChild0 = (_mm_movemask_ps(lrhit) & 1) && (__float_as_uint(cnodes.z) & PATH_RAY_SHADOW);
- traverseChild1 = (_mm_movemask_ps(lrhit) & 2) && (__float_as_uint(cnodes.w) & PATH_RAY_SHADOW);
+ traverseChild0 = (movemask(lrhit) & 1) && (__float_as_uint(cnodes.z) & PATH_RAY_SHADOW);
+ traverseChild1 = (movemask(lrhit) & 2) && (__float_as_uint(cnodes.w) & PATH_RAY_SHADOW);
#else
- traverseChild0 = (_mm_movemask_ps(lrhit) & 1);
- traverseChild1 = (_mm_movemask_ps(lrhit) & 2);
+ traverseChild0 = (movemask(lrhit) & 1);
+ traverseChild1 = (movemask(lrhit) & 2);
#endif
#endif // __KERNEL_SSE2__
@@ -164,9 +164,7 @@ ccl_device bool BVH_FUNCTION_NAME
#if !defined(__KERNEL_SSE2__)
bool closestChild1 = (c1min < c0min);
#else
- union { __m128 m128; float v[4]; } uminmax;
- uminmax.m128 = tminmax;
- bool closestChild1 = uminmax.v[1] < uminmax.v[0];
+ bool closestChild1 = tminmax[1] < tminmax[0];
#endif
if(closestChild1) {
@@ -254,8 +252,7 @@ ccl_device bool BVH_FUNCTION_NAME
if(kernel_tex_fetch(__prim_type, isect_array->prim) & PRIMITIVE_ALL_TRIANGLE)
#endif
{
- float4 Ns = kernel_tex_fetch(__tri_normal, prim);
- shader = __float_as_int(Ns.w);
+ shader = __float_as_int(kernel_tex_fetch(__tri_shader, prim));
}
#ifdef __HAIR__
else {
@@ -301,12 +298,12 @@ ccl_device bool BVH_FUNCTION_NAME
num_hits_in_instance = 0;
#if defined(__KERNEL_SSE2__)
- Psplat[0] = _mm_set_ps1(P.x);
- Psplat[1] = _mm_set_ps1(P.y);
- Psplat[2] = _mm_set_ps1(P.z);
+ Psplat[0] = ssef(P.x);
+ Psplat[1] = ssef(P.y);
+ Psplat[2] = ssef(P.z);
isect_array->t = isect_t;
- tsplat = _mm_set_ps(-isect_t, -isect_t, 0.0f, 0.0f);
+ tsplat = ssef(0.0f, 0.0f, -isect_t, -isect_t);
gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
#endif
@@ -348,13 +345,13 @@ ccl_device bool BVH_FUNCTION_NAME
}
#if defined(__KERNEL_SSE2__)
- Psplat[0] = _mm_set_ps1(P.x);
- Psplat[1] = _mm_set_ps1(P.y);
- Psplat[2] = _mm_set_ps1(P.z);
+ Psplat[0] = ssef(P.x);
+ Psplat[1] = ssef(P.y);
+ Psplat[2] = ssef(P.z);
isect_t = tmax;
isect_array->t = isect_t;
- tsplat = _mm_set_ps(-isect_t, -isect_t, 0.0f, 0.0f);
+ tsplat = ssef(0.0f, 0.0f, -isect_t, -isect_t);
gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
#endif
diff --git a/intern/cycles/kernel/geom/geom_bvh_subsurface.h b/intern/cycles/kernel/geom/geom_bvh_subsurface.h
index a19f05dd371..a8f57cffa78 100644
--- a/intern/cycles/kernel/geom/geom_bvh_subsurface.h
+++ b/intern/cycles/kernel/geom/geom_bvh_subsurface.h
@@ -65,15 +65,15 @@ ccl_device uint BVH_FUNCTION_NAME(KernelGlobals *kg, const Ray *ray, Intersectio
const shuffle_swap_t shuf_identity = shuffle_swap_identity();
const shuffle_swap_t shuf_swap = shuffle_swap_swap();
- const __m128 pn = _mm_castsi128_ps(_mm_set_epi32(0x80000000, 0x80000000, 0, 0));
- __m128 Psplat[3], idirsplat[3];
+ const ssef pn = cast(ssei(0, 0, 0x80000000, 0x80000000));
+ ssef Psplat[3], idirsplat[3];
shuffle_swap_t shufflexyz[3];
- Psplat[0] = _mm_set_ps1(P.x);
- Psplat[1] = _mm_set_ps1(P.y);
- Psplat[2] = _mm_set_ps1(P.z);
+ Psplat[0] = ssef(P.x);
+ Psplat[1] = ssef(P.y);
+ Psplat[2] = ssef(P.z);
- __m128 tsplat = _mm_set_ps(-isect_t, -isect_t, 0.0f, 0.0f);
+ ssef tsplat(0.0f, 0.0f, -isect_t, -isect_t);
gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
#endif
@@ -131,25 +131,27 @@ ccl_device uint BVH_FUNCTION_NAME(KernelGlobals *kg, const Ray *ray, Intersectio
/* Intersect two child bounding boxes, SSE3 version adapted from Embree */
/* fetch node data */
- const __m128 *bvh_nodes = (__m128*)kg->__bvh_nodes.data + nodeAddr*BVH_NODE_SIZE;
+ const ssef *bvh_nodes = (ssef*)kg->__bvh_nodes.data + nodeAddr*BVH_NODE_SIZE;
const float4 cnodes = ((float4*)bvh_nodes)[3];
/* intersect ray against child nodes */
- const __m128 tminmaxx = _mm_mul_ps(_mm_sub_ps(shuffle_swap(bvh_nodes[0], shufflexyz[0]), Psplat[0]), idirsplat[0]);
- const __m128 tminmaxy = _mm_mul_ps(_mm_sub_ps(shuffle_swap(bvh_nodes[1], shufflexyz[1]), Psplat[1]), idirsplat[1]);
- const __m128 tminmaxz = _mm_mul_ps(_mm_sub_ps(shuffle_swap(bvh_nodes[2], shufflexyz[2]), Psplat[2]), idirsplat[2]);
+ const ssef tminmaxx = (shuffle_swap(bvh_nodes[0], shufflexyz[0]) - Psplat[0]) * idirsplat[0];
+ const ssef tminmaxy = (shuffle_swap(bvh_nodes[1], shufflexyz[1]) - Psplat[1]) * idirsplat[1];
+ const ssef tminmaxz = (shuffle_swap(bvh_nodes[2], shufflexyz[2]) - Psplat[2]) * idirsplat[2];
- const __m128 tminmax = _mm_xor_ps(_mm_max_ps(_mm_max_ps(tminmaxx, tminmaxy), _mm_max_ps(tminmaxz, tsplat)), pn);
- const __m128 lrhit = _mm_cmple_ps(tminmax, shuffle<2, 3, 0, 1>(tminmax));
+ /* calculate { c0min, c1min, -c0max, -c1max} */
+ const ssef minmax = max(max(tminmaxx, tminmaxy), max(tminmaxz, tsplat));
+ const ssef tminmax = minmax ^ pn;
+ const sseb lrhit = tminmax <= shuffle<2, 3, 0, 1>(tminmax);
/* decide which nodes to traverse next */
#ifdef __VISIBILITY_FLAG__
/* this visibility test gives a 5% performance hit, how to solve? */
- traverseChild0 = (_mm_movemask_ps(lrhit) & 1) && (__float_as_uint(cnodes.z) & visibility);
- traverseChild1 = (_mm_movemask_ps(lrhit) & 2) && (__float_as_uint(cnodes.w) & visibility);
+ traverseChild0 = (movemask(lrhit) & 1) && (__float_as_uint(cnodes.z) & visibility);
+ traverseChild1 = (movemask(lrhit) & 2) && (__float_as_uint(cnodes.w) & visibility);
#else
- traverseChild0 = (_mm_movemask_ps(lrhit) & 1);
- traverseChild1 = (_mm_movemask_ps(lrhit) & 2);
+ traverseChild0 = (movemask(lrhit) & 1);
+ traverseChild1 = (movemask(lrhit) & 2);
#endif
#endif // __KERNEL_SSE2__
@@ -161,9 +163,7 @@ ccl_device uint BVH_FUNCTION_NAME(KernelGlobals *kg, const Ray *ray, Intersectio
#if !defined(__KERNEL_SSE2__)
bool closestChild1 = (c1min < c0min);
#else
- union { __m128 m128; float v[4]; } uminmax;
- uminmax.m128 = tminmax;
- bool closestChild1 = uminmax.v[1] < uminmax.v[0];
+ bool closestChild1 = tminmax[1] < tminmax[0];
#endif
if(closestChild1) {
@@ -243,11 +243,11 @@ ccl_device uint BVH_FUNCTION_NAME(KernelGlobals *kg, const Ray *ray, Intersectio
#endif
#if defined(__KERNEL_SSE2__)
- Psplat[0] = _mm_set_ps1(P.x);
- Psplat[1] = _mm_set_ps1(P.y);
- Psplat[2] = _mm_set_ps1(P.z);
+ Psplat[0] = ssef(P.x);
+ Psplat[1] = ssef(P.y);
+ Psplat[2] = ssef(P.z);
- tsplat = _mm_set_ps(-isect_t, -isect_t, 0.0f, 0.0f);
+ tsplat = ssef(0.0f, 0.0f, -isect_t, -isect_t);
gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
#endif
@@ -279,11 +279,11 @@ ccl_device uint BVH_FUNCTION_NAME(KernelGlobals *kg, const Ray *ray, Intersectio
#endif
#if defined(__KERNEL_SSE2__)
- Psplat[0] = _mm_set_ps1(P.x);
- Psplat[1] = _mm_set_ps1(P.y);
- Psplat[2] = _mm_set_ps1(P.z);
+ Psplat[0] = ssef(P.x);
+ Psplat[1] = ssef(P.y);
+ Psplat[2] = ssef(P.z);
- tsplat = _mm_set_ps(-isect_t, -isect_t, 0.0f, 0.0f);
+ tsplat = ssef(0.0f, 0.0f, -isect_t, -isect_t);
gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
#endif
diff --git a/intern/cycles/kernel/geom/geom_bvh_traversal.h b/intern/cycles/kernel/geom/geom_bvh_traversal.h
index 9fd40f91471..e39228c33de 100644
--- a/intern/cycles/kernel/geom/geom_bvh_traversal.h
+++ b/intern/cycles/kernel/geom/geom_bvh_traversal.h
@@ -72,15 +72,15 @@ ccl_device bool BVH_FUNCTION_NAME
const shuffle_swap_t shuf_identity = shuffle_swap_identity();
const shuffle_swap_t shuf_swap = shuffle_swap_swap();
- const __m128 pn = _mm_castsi128_ps(_mm_set_epi32(0x80000000, 0x80000000, 0, 0));
- __m128 Psplat[3], idirsplat[3];
+ const ssef pn = cast(ssei(0, 0, 0x80000000, 0x80000000));
+ ssef Psplat[3], idirsplat[3];
shuffle_swap_t shufflexyz[3];
- Psplat[0] = _mm_set_ps1(P.x);
- Psplat[1] = _mm_set_ps1(P.y);
- Psplat[2] = _mm_set_ps1(P.z);
+ Psplat[0] = ssef(P.x);
+ Psplat[1] = ssef(P.y);
+ Psplat[2] = ssef(P.z);
- __m128 tsplat = _mm_set_ps(-isect->t, -isect->t, 0.0f, 0.0f);
+ ssef tsplat(0.0f, 0.0f, -isect->t, -isect->t);
gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
#endif
@@ -151,17 +151,17 @@ ccl_device bool BVH_FUNCTION_NAME
/* Intersect two child bounding boxes, SSE3 version adapted from Embree */
/* fetch node data */
- const __m128 *bvh_nodes = (__m128*)kg->__bvh_nodes.data + nodeAddr*BVH_NODE_SIZE;
+ const ssef *bvh_nodes = (ssef*)kg->__bvh_nodes.data + nodeAddr*BVH_NODE_SIZE;
const float4 cnodes = ((float4*)bvh_nodes)[3];
/* intersect ray against child nodes */
- const __m128 tminmaxx = _mm_mul_ps(_mm_sub_ps(shuffle_swap(bvh_nodes[0], shufflexyz[0]), Psplat[0]), idirsplat[0]);
- const __m128 tminmaxy = _mm_mul_ps(_mm_sub_ps(shuffle_swap(bvh_nodes[1], shufflexyz[1]), Psplat[1]), idirsplat[1]);
- const __m128 tminmaxz = _mm_mul_ps(_mm_sub_ps(shuffle_swap(bvh_nodes[2], shufflexyz[2]), Psplat[2]), idirsplat[2]);
+ const ssef tminmaxx = (shuffle_swap(bvh_nodes[0], shufflexyz[0]) - Psplat[0]) * idirsplat[0];
+ const ssef tminmaxy = (shuffle_swap(bvh_nodes[1], shufflexyz[1]) - Psplat[1]) * idirsplat[1];
+ const ssef tminmaxz = (shuffle_swap(bvh_nodes[2], shufflexyz[2]) - Psplat[2]) * idirsplat[2];
/* calculate { c0min, c1min, -c0max, -c1max} */
- __m128 minmax = _mm_max_ps(_mm_max_ps(tminmaxx, tminmaxy), _mm_max_ps(tminmaxz, tsplat));
- const __m128 tminmax = _mm_xor_ps(minmax, pn);
+ ssef minmax = max(max(tminmaxx, tminmaxy), max(tminmaxz, tsplat));
+ const ssef tminmax = minmax ^ pn;
#if FEATURE(BVH_HAIR_MINIMUM_WIDTH)
if(difl != 0.0f) {
@@ -182,16 +182,16 @@ ccl_device bool BVH_FUNCTION_NAME
}
#endif
- const __m128 lrhit = _mm_cmple_ps(tminmax, shuffle<2, 3, 0, 1>(tminmax));
+ const sseb lrhit = tminmax <= shuffle<2, 3, 0, 1>(tminmax);
/* decide which nodes to traverse next */
#ifdef __VISIBILITY_FLAG__
/* this visibility test gives a 5% performance hit, how to solve? */
- traverseChild0 = (_mm_movemask_ps(lrhit) & 1) && (__float_as_uint(cnodes.z) & visibility);
- traverseChild1 = (_mm_movemask_ps(lrhit) & 2) && (__float_as_uint(cnodes.w) & visibility);
+ traverseChild0 = (movemask(lrhit) & 1) && (__float_as_uint(cnodes.z) & visibility);
+ traverseChild1 = (movemask(lrhit) & 2) && (__float_as_uint(cnodes.w) & visibility);
#else
- traverseChild0 = (_mm_movemask_ps(lrhit) & 1);
- traverseChild1 = (_mm_movemask_ps(lrhit) & 2);
+ traverseChild0 = (movemask(lrhit) & 1);
+ traverseChild1 = (movemask(lrhit) & 2);
#endif
#endif // __KERNEL_SSE2__
@@ -203,9 +203,7 @@ ccl_device bool BVH_FUNCTION_NAME
#if !defined(__KERNEL_SSE2__)
bool closestChild1 = (c1min < c0min);
#else
- union { __m128 m128; float v[4]; } uminmax;
- uminmax.m128 = tminmax;
- bool closestChild1 = uminmax.v[1] < uminmax.v[0];
+ bool closestChild1 = tminmax[1] < tminmax[0];
#endif
if(closestChild1) {
@@ -282,7 +280,7 @@ ccl_device bool BVH_FUNCTION_NAME
if(visibility == PATH_RAY_SHADOW_OPAQUE)
return true;
- tsplat = _mm_set_ps(-isect->t, -isect->t, 0.0f, 0.0f);
+ tsplat = ssef(0.0f, 0.0f, -isect->t, -isect->t);
}
#else
if(hit && visibility == PATH_RAY_SHADOW_OPAQUE)
@@ -304,11 +302,11 @@ ccl_device bool BVH_FUNCTION_NAME
#endif
#if defined(__KERNEL_SSE2__)
- Psplat[0] = _mm_set_ps1(P.x);
- Psplat[1] = _mm_set_ps1(P.y);
- Psplat[2] = _mm_set_ps1(P.z);
+ Psplat[0] = ssef(P.x);
+ Psplat[1] = ssef(P.y);
+ Psplat[2] = ssef(P.z);
- tsplat = _mm_set_ps(-isect->t, -isect->t, 0.0f, 0.0f);
+ tsplat = ssef(0.0f, 0.0f, -isect->t, -isect->t);
gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
#endif
@@ -334,11 +332,11 @@ ccl_device bool BVH_FUNCTION_NAME
#endif
#if defined(__KERNEL_SSE2__)
- Psplat[0] = _mm_set_ps1(P.x);
- Psplat[1] = _mm_set_ps1(P.y);
- Psplat[2] = _mm_set_ps1(P.z);
+ Psplat[0] = ssef(P.x);
+ Psplat[1] = ssef(P.y);
+ Psplat[2] = ssef(P.z);
- tsplat = _mm_set_ps(-isect->t, -isect->t, 0.0f, 0.0f);
+ tsplat = ssef(0.0f, 0.0f, -isect->t, -isect->t);
gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
#endif
diff --git a/intern/cycles/kernel/geom/geom_curve.h b/intern/cycles/kernel/geom/geom_curve.h
index dabfb0c72c8..863836ffcea 100644
--- a/intern/cycles/kernel/geom/geom_curve.h
+++ b/intern/cycles/kernel/geom/geom_curve.h
@@ -214,9 +214,9 @@ ccl_device_inline void curvebounds(float *lower, float *upper, float *extremta,
}
#ifdef __KERNEL_SSE2__
-ccl_device_inline __m128 transform_point_T3(const __m128 t[3], const __m128 &a)
+ccl_device_inline ssef transform_point_T3(const ssef t[3], const ssef &a)
{
- return fma(broadcast<0>(a), t[0], fma(broadcast<1>(a), t[1], _mm_mul_ps(broadcast<2>(a), t[2])));
+ return madd(shuffle<0>(a), t[0], madd(shuffle<1>(a), t[1], shuffle<2>(a) * t[2]));
}
#endif
@@ -238,16 +238,16 @@ ccl_device_inline bool bvh_cardinal_curve_intersect(KernelGlobals *kg, Intersect
int prim = kernel_tex_fetch(__prim_index, curveAddr);
#ifdef __KERNEL_SSE2__
- __m128 vdir = load_m128(dir);
- __m128 vcurve_coef[4];
+ ssef vdir = load4f(dir);
+ ssef vcurve_coef[4];
const float3 *curve_coef = (float3 *)vcurve_coef;
{
- __m128 dtmp = _mm_mul_ps(vdir, vdir);
- __m128 d_ss = _mm_sqrt_ss(_mm_add_ss(dtmp, broadcast<2>(dtmp)));
- __m128 rd_ss = _mm_div_ss(_mm_set_ss(1.0f), d_ss);
+ ssef dtmp = vdir * vdir;
+ ssef d_ss = mm_sqrt(dtmp + shuffle<2>(dtmp));
+ ssef rd_ss = load1f_first(1.0f) / d_ss;
- __m128i v00vec = _mm_load_si128((__m128i *)&kg->__curves.data[prim]);
+ ssei v00vec = load4i((ssei *)&kg->__curves.data[prim]);
int2 &v00 = (int2 &)v00vec;
int k0 = v00.x + segment;
@@ -255,44 +255,44 @@ ccl_device_inline bool bvh_cardinal_curve_intersect(KernelGlobals *kg, Intersect
int ka = max(k0 - 1, v00.x);
int kb = min(k1 + 1, v00.x + v00.y - 1);
- __m128 P_curve[4];
+ ssef P_curve[4];
if(type & PRIMITIVE_CURVE) {
- P_curve[0] = _mm_load_ps(&kg->__curve_keys.data[ka].x);
- P_curve[1] = _mm_load_ps(&kg->__curve_keys.data[k0].x);
- P_curve[2] = _mm_load_ps(&kg->__curve_keys.data[k1].x);
- P_curve[3] = _mm_load_ps(&kg->__curve_keys.data[kb].x);
+ P_curve[0] = load4f(&kg->__curve_keys.data[ka].x);
+ P_curve[1] = load4f(&kg->__curve_keys.data[k0].x);
+ P_curve[2] = load4f(&kg->__curve_keys.data[k1].x);
+ P_curve[3] = load4f(&kg->__curve_keys.data[kb].x);
}
else {
int fobject = (object == OBJECT_NONE)? kernel_tex_fetch(__prim_object, curveAddr): object;
motion_cardinal_curve_keys(kg, fobject, prim, time, ka, k0, k1, kb, (float4*)&P_curve);
}
- __m128 rd_sgn = set_sign_bit<0, 1, 1, 1>(broadcast<0>(rd_ss));
- __m128 mul_zxxy = _mm_mul_ps(shuffle<2, 0, 0, 1>(vdir), rd_sgn);
- __m128 mul_yz = _mm_mul_ps(shuffle<1, 2, 1, 2>(vdir), mul_zxxy);
- __m128 mul_shuf = shuffle<0, 1, 2, 3>(mul_zxxy, mul_yz);
- __m128 vdir0 = _mm_and_ps(vdir, _mm_castsi128_ps(_mm_setr_epi32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0)));
+ ssef rd_sgn = set_sign_bit<0, 1, 1, 1>(shuffle<0>(rd_ss));
+ ssef mul_zxxy = shuffle<2, 0, 0, 1>(vdir) * rd_sgn;
+ ssef mul_yz = shuffle<1, 2, 1, 2>(vdir) * mul_zxxy;
+ ssef mul_shuf = shuffle<0, 1, 2, 3>(mul_zxxy, mul_yz);
+ ssef vdir0 = vdir & cast(ssei(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0));
- __m128 htfm0 = shuffle<0, 2, 0, 3>(mul_shuf, vdir0);
- __m128 htfm1 = shuffle<1, 0, 1, 3>(_mm_set_ss(_mm_cvtss_f32(d_ss)), vdir0);
- __m128 htfm2 = shuffle<1, 3, 2, 3>(mul_shuf, vdir0);
+ ssef htfm0 = shuffle<0, 2, 0, 3>(mul_shuf, vdir0);
+ ssef htfm1 = shuffle<1, 0, 1, 3>(load1f_first(extract<0>(d_ss)), vdir0);
+ ssef htfm2 = shuffle<1, 3, 2, 3>(mul_shuf, vdir0);
- __m128 htfm[] = { htfm0, htfm1, htfm2 };
- __m128 vP = load_m128(P);
- __m128 p0 = transform_point_T3(htfm, _mm_sub_ps(P_curve[0], vP));
- __m128 p1 = transform_point_T3(htfm, _mm_sub_ps(P_curve[1], vP));
- __m128 p2 = transform_point_T3(htfm, _mm_sub_ps(P_curve[2], vP));
- __m128 p3 = transform_point_T3(htfm, _mm_sub_ps(P_curve[3], vP));
+ ssef htfm[] = { htfm0, htfm1, htfm2 };
+ ssef vP = load4f(P);
+ ssef p0 = transform_point_T3(htfm, P_curve[0] - vP);
+ ssef p1 = transform_point_T3(htfm, P_curve[1] - vP);
+ ssef p2 = transform_point_T3(htfm, P_curve[2] - vP);
+ ssef p3 = transform_point_T3(htfm, P_curve[3] - vP);
float fc = 0.71f;
- __m128 vfc = _mm_set1_ps(fc);
- __m128 vfcxp3 = _mm_mul_ps(vfc, p3);
+ ssef vfc = ssef(fc);
+ ssef vfcxp3 = vfc * p3;
vcurve_coef[0] = p1;
- vcurve_coef[1] = _mm_mul_ps(vfc, _mm_sub_ps(p2, p0));
- vcurve_coef[2] = fma(_mm_set1_ps(fc * 2.0f), p0, fma(_mm_set1_ps(fc - 3.0f), p1, fms(_mm_set1_ps(3.0f - 2.0f * fc), p2, vfcxp3)));
- vcurve_coef[3] = fms(_mm_set1_ps(fc - 2.0f), _mm_sub_ps(p2, p1), fms(vfc, p0, vfcxp3));
+ vcurve_coef[1] = vfc * (p2 - p0);
+ vcurve_coef[2] = madd(ssef(fc * 2.0f), p0, madd(ssef(fc - 3.0f), p1, msub(ssef(3.0f - 2.0f * fc), p2, vfcxp3)));
+ vcurve_coef[3] = msub(ssef(fc - 2.0f), p2 - p1, msub(vfc, p0, vfcxp3));
r_st = ((float4 &)P_curve[1]).w;
r_en = ((float4 &)P_curve[2]).w;
@@ -386,12 +386,12 @@ ccl_device_inline bool bvh_cardinal_curve_intersect(KernelGlobals *kg, Intersect
float i_st = tree * resol;
float i_en = i_st + (level * resol);
#ifdef __KERNEL_SSE2__
- __m128 vi_st = _mm_set1_ps(i_st), vi_en = _mm_set1_ps(i_en);
- __m128 vp_st = fma(fma(fma(vcurve_coef[3], vi_st, vcurve_coef[2]), vi_st, vcurve_coef[1]), vi_st, vcurve_coef[0]);
- __m128 vp_en = fma(fma(fma(vcurve_coef[3], vi_en, vcurve_coef[2]), vi_en, vcurve_coef[1]), vi_en, vcurve_coef[0]);
+ ssef vi_st = ssef(i_st), vi_en = ssef(i_en);
+ ssef vp_st = madd(madd(madd(vcurve_coef[3], vi_st, vcurve_coef[2]), vi_st, vcurve_coef[1]), vi_st, vcurve_coef[0]);
+ ssef vp_en = madd(madd(madd(vcurve_coef[3], vi_en, vcurve_coef[2]), vi_en, vcurve_coef[1]), vi_en, vcurve_coef[0]);
- __m128 vbmin = _mm_min_ps(vp_st, vp_en);
- __m128 vbmax = _mm_max_ps(vp_st, vp_en);
+ ssef vbmin = min(vp_st, vp_en);
+ ssef vbmax = max(vp_st, vp_en);
float3 &bmin = (float3 &)vbmin, &bmax = (float3 &)vbmax;
float &bminx = bmin.x, &bminy = bmin.y, &bminz = bmin.z;
@@ -678,38 +678,38 @@ ccl_device_inline bool bvh_curve_intersect(KernelGlobals *kg, Intersection *isec
float sphere_b_tmp = dot3(dir, sphere_dif1);
float3 sphere_dif2 = sphere_dif1 - sphere_b_tmp * dir;
#else
- __m128 P_curve[2];
+ ssef P_curve[2];
if(type & PRIMITIVE_CURVE) {
- P_curve[0] = _mm_load_ps(&kg->__curve_keys.data[k0].x);
- P_curve[1] = _mm_load_ps(&kg->__curve_keys.data[k1].x);
+ P_curve[0] = load4f(&kg->__curve_keys.data[k0].x);
+ P_curve[1] = load4f(&kg->__curve_keys.data[k1].x);
}
else {
int fobject = (object == OBJECT_NONE)? kernel_tex_fetch(__prim_object, curveAddr): object;
motion_curve_keys(kg, fobject, prim, time, k0, k1, (float4*)&P_curve);
}
- const __m128 or12 = shuffle<3, 3, 3, 3>(P_curve[0], P_curve[1]);
+ const ssef or12 = shuffle<3, 3, 3, 3>(P_curve[0], P_curve[1]);
- __m128 r12 = or12;
- const __m128 vP = load_m128(P);
- const __m128 dif = _mm_sub_ps(vP, P_curve[0]);
- const __m128 dif_second = _mm_sub_ps(vP, P_curve[1]);
+ ssef r12 = or12;
+ const ssef vP = load4f(P);
+ const ssef dif = vP - P_curve[0];
+ const ssef dif_second = vP - P_curve[1];
if(difl != 0.0f) {
- const __m128 len1_sq = len3_squared_splat(dif);
- const __m128 len2_sq = len3_squared_splat(dif_second);
- const __m128 len12 = _mm_sqrt_ps(shuffle<0, 0, 0, 0>(len1_sq, len2_sq));
- const __m128 pixelsize12 = _mm_min_ps(_mm_mul_ps(len12, _mm_set1_ps(difl)), _mm_set1_ps(extmax));
- r12 = _mm_max_ps(or12, pixelsize12);
+ const ssef len1_sq = len3_squared_splat(dif);
+ const ssef len2_sq = len3_squared_splat(dif_second);
+ const ssef len12 = mm_sqrt(shuffle<0, 0, 0, 0>(len1_sq, len2_sq));
+ const ssef pixelsize12 = min(len12 * difl, ssef(extmax));
+ r12 = max(or12, pixelsize12);
}
- float or1 = _mm_cvtss_f32(or12), or2 = _mm_cvtss_f32(broadcast<2>(or12));
- float r1 = _mm_cvtss_f32(r12), r2 = _mm_cvtss_f32(broadcast<2>(r12));
-
- const __m128 p21_diff = _mm_sub_ps(P_curve[1], P_curve[0]);
- const __m128 sphere_dif1 = _mm_mul_ps(_mm_add_ps(dif, dif_second), _mm_set1_ps(0.5f));
- const __m128 dir = load_m128(direction);
- const __m128 sphere_b_tmp = dot3_splat(dir, sphere_dif1);
- const __m128 sphere_dif2 = fnma(sphere_b_tmp, dir, sphere_dif1);
+ float or1 = extract<0>(or12), or2 = extract<0>(shuffle<2>(or12));
+ float r1 = extract<0>(r12), r2 = extract<0>(shuffle<2>(r12));
+
+ const ssef p21_diff = P_curve[1] - P_curve[0];
+ const ssef sphere_dif1 = (dif + dif_second) * 0.5f;
+ const ssef dir = load4f(direction);
+ const ssef sphere_b_tmp = dot3_splat(dir, sphere_dif1);
+ const ssef sphere_dif2 = nmsub(sphere_b_tmp, dir, sphere_dif1);
#endif
float mr = max(r1, r2);
@@ -727,7 +727,7 @@ ccl_device_inline bool bvh_curve_intersect(KernelGlobals *kg, Intersection *isec
#ifndef __KERNEL_SSE2__
float3 tg = p21_diff * invl;
#else
- const __m128 tg = _mm_mul_ps(p21_diff, _mm_set1_ps(invl));
+ const ssef tg = p21_diff * invl;
#endif
float gd = (r2 - r1) * invl;
@@ -751,7 +751,7 @@ ccl_device_inline bool bvh_curve_intersect(KernelGlobals *kg, Intersection *isec
float3 cprod = cross(tg, dir);
float cprod2sq = len3_squared(cross(tg, dif));
#else
- const __m128 cprod = cross(tg, dir);
+ const ssef cprod = cross(tg, dir);
float cprod2sq = len3_squared(cross_zxy(tg, dif));
#endif
float cprodsq = len3_squared(cprod);
@@ -769,7 +769,7 @@ ccl_device_inline bool bvh_curve_intersect(KernelGlobals *kg, Intersection *isec
#ifndef __KERNEL_SSE2__
float3 tdif = dif + tcentre * dir;
#else
- const __m128 tdif = fma(_mm_set1_ps(tcentre), dir, dif);
+ const ssef tdif = madd(ssef(tcentre), dir, dif);
#endif
float tdifz = dot3(tdif, tg);
float tdifma = tdifz*gd + r1;
diff --git a/intern/cycles/kernel/geom/geom_motion_triangle.h b/intern/cycles/kernel/geom/geom_motion_triangle.h
index 73fcf1adda6..5ab0b731bdd 100644
--- a/intern/cycles/kernel/geom/geom_motion_triangle.h
+++ b/intern/cycles/kernel/geom/geom_motion_triangle.h
@@ -233,8 +233,7 @@ ccl_device_inline float3 motion_triangle_refine_subsurface(KernelGlobals *kg, Sh
ccl_device_noinline void motion_triangle_shader_setup(KernelGlobals *kg, ShaderData *sd, const Intersection *isect, const Ray *ray, bool subsurface)
{
/* get shader */
- float4 Ns = kernel_tex_fetch(__tri_normal, sd->prim);
- sd->shader = __float_as_int(Ns.w);
+ sd->shader = __float_as_int(kernel_tex_fetch(__tri_shader, sd->prim));
/* get motion info */
int numsteps, numverts;
diff --git a/intern/cycles/kernel/geom/geom_triangle.h b/intern/cycles/kernel/geom/geom_triangle.h
index 355e36fef0c..f2f35c2efd0 100644
--- a/intern/cycles/kernel/geom/geom_triangle.h
+++ b/intern/cycles/kernel/geom/geom_triangle.h
@@ -116,6 +116,20 @@ ccl_device_inline float3 triangle_refine_subsurface(KernelGlobals *kg, ShaderDat
#endif
}
+/* normal on triangle */
+ccl_device_inline float3 triangle_normal(KernelGlobals *kg, int prim)
+{
+ /* load triangle vertices */
+ float3 tri_vindex = float4_to_float3(kernel_tex_fetch(__tri_vindex, prim));
+
+ float3 v0 = float4_to_float3(kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.x)));
+ float3 v1 = float4_to_float3(kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.y)));
+ float3 v2 = float4_to_float3(kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.z)));
+
+ /* return normal */
+ return normalize(cross(v1 - v0, v2 - v0));
+}
+
/* point and normal on triangle */
ccl_device_inline void triangle_point_normal(KernelGlobals *kg, int prim, float u, float v, float3 *P, float3 *Ng, int *shader)
{
@@ -130,9 +144,11 @@ ccl_device_inline void triangle_point_normal(KernelGlobals *kg, int prim, float
float t = 1.0f - u - v;
*P = (u*v0 + v*v1 + t*v2);
- float4 Nm = kernel_tex_fetch(__tri_normal, prim);
- *Ng = make_float3(Nm.x, Nm.y, Nm.z);
- *shader = __float_as_int(Nm.w);
+ /* compute normal */
+ *Ng = normalize(cross(v1 - v0, v2 - v0));
+
+ /* shader`*/
+ *shader = __float_as_int(kernel_tex_fetch(__tri_shader, prim));
}
/* Triangle vertex locations */
@@ -243,11 +259,20 @@ ccl_device float3 triangle_attribute_float3(KernelGlobals *kg, const ShaderData
return sd->u*f0 + sd->v*f1 + (1.0f - sd->u - sd->v)*f2;
}
- else if(elem == ATTR_ELEMENT_CORNER) {
+ else if(elem == ATTR_ELEMENT_CORNER || elem == ATTR_ELEMENT_CORNER_BYTE) {
int tri = offset + sd->prim*3;
- float3 f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, tri + 0));
- float3 f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, tri + 1));
- float3 f2 = float4_to_float3(kernel_tex_fetch(__attributes_float3, tri + 2));
+ float3 f0, f1, f2;
+
+ if(elem == ATTR_ELEMENT_CORNER) {
+ f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, tri + 0));
+ f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, tri + 1));
+ f2 = float4_to_float3(kernel_tex_fetch(__attributes_float3, tri + 2));
+ }
+ else {
+ f0 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, tri + 0));
+ f1 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, tri + 1));
+ f2 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, tri + 2));
+ }
#ifdef __RAY_DIFFERENTIALS__
if(dx) *dx = sd->du.dx*f0 + sd->dv.dx*f1 - (sd->du.dx + sd->dv.dx)*f2;
diff --git a/intern/cycles/kernel/kernel.h b/intern/cycles/kernel/kernel.h
index b169b15b9b5..264e5e3e4d0 100644
--- a/intern/cycles/kernel/kernel.h
+++ b/intern/cycles/kernel/kernel.h
@@ -87,6 +87,17 @@ void kernel_cpu_avx_shader(KernelGlobals *kg, uint4 *input, float4 *output,
int type, int i, int sample);
#endif
+#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2
+void kernel_cpu_avx2_path_trace(KernelGlobals *kg, float *buffer, unsigned int *rng_state,
+ int sample, int x, int y, int offset, int stride);
+void kernel_cpu_avx2_convert_to_byte(KernelGlobals *kg, uchar4 *rgba, float *buffer,
+ float sample_scale, int x, int y, int offset, int stride);
+void kernel_cpu_avx2_convert_to_half_float(KernelGlobals *kg, uchar4 *rgba, float *buffer,
+ float sample_scale, int x, int y, int offset, int stride);
+void kernel_cpu_avx2_shader(KernelGlobals *kg, uint4 *input, float4 *output,
+ int type, int i, int sample);
+#endif
+
CCL_NAMESPACE_END
#endif /* __KERNEL_H__ */
diff --git a/intern/cycles/kernel/kernel_avx.cpp b/intern/cycles/kernel/kernel_avx.cpp
index f5e1b8a7bb7..d612a82b785 100644
--- a/intern/cycles/kernel/kernel_avx.cpp
+++ b/intern/cycles/kernel/kernel_avx.cpp
@@ -24,6 +24,7 @@
#define __KERNEL_SSE3__
#define __KERNEL_SSSE3__
#define __KERNEL_SSE41__
+#define __KERNEL_AVX__
#endif
#include "util_optimization.h"
diff --git a/intern/cycles/kernel/kernel_avx2.cpp b/intern/cycles/kernel/kernel_avx2.cpp
new file mode 100644
index 00000000000..339421a002b
--- /dev/null
+++ b/intern/cycles/kernel/kernel_avx2.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2011-2014 Blender Foundation
+ *
+ * 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
+ */
+
+/* Optimized CPU kernel entry points. This file is compiled with AVX2
+ * optimization flags and nearly all functions inlined, while kernel.cpp
+ * is compiled without for other CPU's. */
+
+/* SSE optimization disabled for now on 32 bit, see bug #36316 */
+#if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86)))
+#define __KERNEL_SSE2__
+#define __KERNEL_SSE3__
+#define __KERNEL_SSSE3__
+#define __KERNEL_SSE41__
+#define __KERNEL_AVX__
+#define __KERNEL_AVX2__
+#endif
+
+#include "util_optimization.h"
+
+#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2
+
+#include "kernel.h"
+#include "kernel_compat_cpu.h"
+#include "kernel_math.h"
+#include "kernel_types.h"
+#include "kernel_globals.h"
+#include "kernel_film.h"
+#include "kernel_path.h"
+#include "kernel_bake.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Path Tracing */
+
+void kernel_cpu_avx2_path_trace(KernelGlobals *kg, float *buffer, unsigned int *rng_state, int sample, int x, int y, int offset, int stride)
+{
+#ifdef __BRANCHED_PATH__
+ if(kernel_data.integrator.branched)
+ kernel_branched_path_trace(kg, buffer, rng_state, sample, x, y, offset, stride);
+ else
+#endif
+ kernel_path_trace(kg, buffer, rng_state, sample, x, y, offset, stride);
+}
+
+/* Film */
+
+void kernel_cpu_avx2_convert_to_byte(KernelGlobals *kg, uchar4 *rgba, float *buffer, float sample_scale, int x, int y, int offset, int stride)
+{
+ kernel_film_convert_to_byte(kg, rgba, buffer, sample_scale, x, y, offset, stride);
+}
+
+void kernel_cpu_avx2_convert_to_half_float(KernelGlobals *kg, uchar4 *rgba, float *buffer, float sample_scale, int x, int y, int offset, int stride)
+{
+ kernel_film_convert_to_half_float(kg, rgba, buffer, sample_scale, x, y, offset, stride);
+}
+
+/* Shader Evaluate */
+
+void kernel_cpu_avx2_shader(KernelGlobals *kg, uint4 *input, float4 *output, int type, int i, int sample)
+{
+ if(type >= SHADER_EVAL_BAKE)
+ kernel_bake_evaluate(kg, input, output, (ShaderEvalType)type, i, sample);
+ else
+ kernel_shader_evaluate(kg, input, output, (ShaderEvalType)type, i, sample);
+}
+
+CCL_NAMESPACE_END
+#else
+
+/* needed for some linkers in combination with scons making empty compilation unit in a library */
+void __dummy_function_cycles_avx2(void);
+void __dummy_function_cycles_avx2(void) {}
+
+#endif
diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h
index e8845e03acb..a2ff6a05706 100644
--- a/intern/cycles/kernel/kernel_bake.h
+++ b/intern/cycles/kernel/kernel_bake.h
@@ -69,7 +69,9 @@ ccl_device void compute_light_pass(KernelGlobals *kg, ShaderData *sd, PathRadian
path_radiance_accum_emission(&L_sample, throughput, emission, state.bounce);
}
- if(kernel_path_integrate_lighting(kg, &rng, sd, &throughput, &state, &L_sample, &ray)) {
+ kernel_path_surface_connect_light(kg, &rng, sd, throughput, &state, &L_sample);
+
+ if(kernel_path_surface_bounce(kg, &rng, sd, &throughput, &state, &L_sample, &ray)) {
#ifdef __LAMP_MIS__
state.ray_t = 0.0f;
#endif
@@ -107,7 +109,17 @@ ccl_device void compute_light_pass(KernelGlobals *kg, ShaderData *sd, PathRadian
path_radiance_accum_emission(&L_sample, throughput, emission, state.bounce);
}
- kernel_branched_path_integrate_lighting(kg, &rng,
+#if defined(__EMISSION__)
+ /* direct light */
+ if(kernel_data.integrator.use_direct_light) {
+ bool all = kernel_data.integrator.sample_all_lights_direct;
+ kernel_branched_path_surface_connect_light(kg, &rng,
+ sd, &state, throughput, 1.0f, &L_sample, all);
+ }
+#endif
+
+ /* indirect light */
+ kernel_branched_path_surface_indirect_light(kg, &rng,
sd, throughput, 1.0f, &state, &L_sample);
}
}
diff --git a/intern/cycles/kernel/kernel_compat_cpu.h b/intern/cycles/kernel/kernel_compat_cpu.h
index d027bb62ebe..c2aab93c87b 100644
--- a/intern/cycles/kernel/kernel_compat_cpu.h
+++ b/intern/cycles/kernel/kernel_compat_cpu.h
@@ -44,16 +44,16 @@ template<typename T> struct texture {
}
#if 0
- ccl_always_inline __m128 fetch_m128(int index)
+ ccl_always_inline ssef fetch_ssef(int index)
{
kernel_assert(index >= 0 && index < width);
- return ((__m128*)data)[index];
+ return ((ssef*)data)[index];
}
- ccl_always_inline __m128i fetch_m128i(int index)
+ ccl_always_inline ssei fetch_ssei(int index)
{
kernel_assert(index >= 0 && index < width);
- return ((__m128i*)data)[index];
+ return ((ssei*)data)[index];
}
#endif
@@ -232,8 +232,8 @@ typedef texture_image<uchar4> texture_image_uchar4;
/* Macros to handle different memory storage on different devices */
#define kernel_tex_fetch(tex, index) (kg->tex.fetch(index))
-#define kernel_tex_fetch_m128(tex, index) (kg->tex.fetch_m128(index))
-#define kernel_tex_fetch_m128i(tex, index) (kg->tex.fetch_m128i(index))
+#define kernel_tex_fetch_ssef(tex, index) (kg->tex.fetch_ssef(index))
+#define kernel_tex_fetch_ssei(tex, index) (kg->tex.fetch_ssei(index))
#define kernel_tex_lookup(tex, t, offset, size) (kg->tex.lookup(t, offset, size))
#define kernel_tex_image_interp(tex, x, y) ((tex < MAX_FLOAT_IMAGES) ? kg->texture_float_images[tex].interp(x, y) : kg->texture_byte_images[tex - MAX_FLOAT_IMAGES].interp(x, y))
#define kernel_tex_image_interp_3d(tex, x, y, z) ((tex < MAX_FLOAT_IMAGES) ? kg->texture_float_images[tex].interp_3d(x, y, z) : kg->texture_byte_images[tex - MAX_FLOAT_IMAGES].interp_3d(x, y, z))
diff --git a/intern/cycles/kernel/kernel_compat_opencl.h b/intern/cycles/kernel/kernel_compat_opencl.h
index 8346b09619e..9e58ebff599 100644
--- a/intern/cycles/kernel/kernel_compat_opencl.h
+++ b/intern/cycles/kernel/kernel_compat_opencl.h
@@ -68,6 +68,9 @@
#ifdef make_int4
#undef make_int4
#endif
+#ifdef make_uchar4
+#undef make_uchar4
+#endif
#define make_float2(x, y) ((float2)(x, y))
#ifdef __CL_NO_FLOAT3__
@@ -79,6 +82,7 @@
#define make_int2(x, y) ((int2)(x, y))
#define make_int3(x, y, z) ((int3)(x, y, z))
#define make_int4(x, y, z, w) ((int4)(x, y, z, w))
+#define make_uchar4(x, y, z, w) ((uchar4)(x, y, z, w))
/* math functions */
#define __uint_as_float(x) as_float(x)
diff --git a/intern/cycles/kernel/kernel_emission.h b/intern/cycles/kernel/kernel_emission.h
index deffa7f2ba2..bda98b84da8 100644
--- a/intern/cycles/kernel/kernel_emission.h
+++ b/intern/cycles/kernel/kernel_emission.h
@@ -63,32 +63,18 @@ ccl_device_noinline float3 direct_emissive_eval(KernelGlobals *kg,
return eval;
}
-ccl_device_noinline bool direct_emission(KernelGlobals *kg, ShaderData *sd, int lindex,
- float randt, float randu, float randv, Ray *ray, BsdfEval *eval,
- bool *is_lamp, int bounce, int transparent_bounce)
+ccl_device_noinline bool direct_emission(KernelGlobals *kg, ShaderData *sd,
+ LightSample *ls, Ray *ray, BsdfEval *eval, bool *is_lamp,
+ int bounce, int transparent_bounce)
{
- LightSample ls;
-
-#ifdef __BRANCHED_PATH__
- if(lindex != LAMP_NONE) {
- /* sample position on a specified light */
- light_select(kg, lindex, randu, randv, sd->P, &ls);
- }
- else
-#endif
- {
- /* sample a light and position on int */
- light_sample(kg, randt, randu, randv, sd->time, sd->P, &ls);
- }
-
- if(ls.pdf == 0.0f)
+ if(ls->pdf == 0.0f)
return false;
/* todo: implement */
differential3 dD = differential3_zero();
/* evaluate closure */
- float3 light_eval = direct_emissive_eval(kg, &ls, -ls.D, dD, ls.t, sd->time, bounce, transparent_bounce);
+ float3 light_eval = direct_emissive_eval(kg, ls, -ls->D, dD, ls->t, sd->time, bounce, transparent_bounce);
if(is_zero(light_eval))
return false;
@@ -98,29 +84,29 @@ ccl_device_noinline bool direct_emission(KernelGlobals *kg, ShaderData *sd, int
#ifdef __VOLUME__
if(sd->prim != PRIM_NONE)
- shader_bsdf_eval(kg, sd, ls.D, eval, &bsdf_pdf);
+ shader_bsdf_eval(kg, sd, ls->D, eval, &bsdf_pdf);
else
- shader_volume_phase_eval(kg, sd, ls.D, eval, &bsdf_pdf);
+ shader_volume_phase_eval(kg, sd, ls->D, eval, &bsdf_pdf);
#else
- shader_bsdf_eval(kg, sd, ls.D, eval, &bsdf_pdf);
+ shader_bsdf_eval(kg, sd, ls->D, eval, &bsdf_pdf);
#endif
- if(ls.shader & SHADER_USE_MIS) {
+ if(ls->shader & SHADER_USE_MIS) {
/* multiple importance sampling */
- float mis_weight = power_heuristic(ls.pdf, bsdf_pdf);
+ float mis_weight = power_heuristic(ls->pdf, bsdf_pdf);
light_eval *= mis_weight;
}
- bsdf_eval_mul(eval, light_eval/ls.pdf);
+ bsdf_eval_mul(eval, light_eval/ls->pdf);
#ifdef __PASSES__
/* use visibility flag to skip lights */
- if(ls.shader & SHADER_EXCLUDE_ANY) {
- if(ls.shader & SHADER_EXCLUDE_DIFFUSE)
+ if(ls->shader & SHADER_EXCLUDE_ANY) {
+ if(ls->shader & SHADER_EXCLUDE_DIFFUSE)
eval->diffuse = make_float3(0.0f, 0.0f, 0.0f);
- if(ls.shader & SHADER_EXCLUDE_GLOSSY)
+ if(ls->shader & SHADER_EXCLUDE_GLOSSY)
eval->glossy = make_float3(0.0f, 0.0f, 0.0f);
- if(ls.shader & SHADER_EXCLUDE_TRANSMIT)
+ if(ls->shader & SHADER_EXCLUDE_TRANSMIT)
eval->transmission = make_float3(0.0f, 0.0f, 0.0f);
}
#endif
@@ -128,19 +114,19 @@ ccl_device_noinline bool direct_emission(KernelGlobals *kg, ShaderData *sd, int
if(bsdf_eval_is_zero(eval))
return false;
- if(ls.shader & SHADER_CAST_SHADOW) {
+ if(ls->shader & SHADER_CAST_SHADOW) {
/* setup ray */
- bool transmit = (dot(sd->Ng, ls.D) < 0.0f);
+ bool transmit = (dot(sd->Ng, ls->D) < 0.0f);
ray->P = ray_offset(sd->P, (transmit)? -sd->Ng: sd->Ng);
- if(ls.t == FLT_MAX) {
+ if(ls->t == FLT_MAX) {
/* distant light */
- ray->D = ls.D;
- ray->t = ls.t;
+ ray->D = ls->D;
+ ray->t = ls->t;
}
else {
/* other lights, avoid self-intersection */
- ray->D = ray_offset(ls.P, ls.Ng) - ray->P;
+ ray->D = ray_offset(ls->P, ls->Ng) - ray->P;
ray->D = normalize_len(ray->D, &ray->t);
}
@@ -153,7 +139,7 @@ ccl_device_noinline bool direct_emission(KernelGlobals *kg, ShaderData *sd, int
}
/* return if it's a lamp for shadow pass */
- *is_lamp = (ls.prim == PRIM_NONE && ls.type != LIGHT_BACKGROUND);
+ *is_lamp = (ls->prim == PRIM_NONE && ls->type != LIGHT_BACKGROUND);
return true;
}
@@ -208,6 +194,17 @@ ccl_device_noinline bool indirect_lamp_emission(KernelGlobals *kg, PathState *st
float3 L = direct_emissive_eval(kg, &ls, -ray->D, ray->dD, ls.t, ray->time, state->bounce, state->transparent_bounce);
+#ifdef __VOLUME__
+ if(state->volume_stack[0].shader != SHADER_NONE) {
+ /* shadow attenuation */
+ Ray volume_ray = *ray;
+ volume_ray.t = ls.t;
+ float3 volume_tp = make_float3(1.0f, 1.0f, 1.0f);
+ kernel_volume_shadow(kg, state, &volume_ray, &volume_tp);
+ L *= volume_tp;
+ }
+#endif
+
if(!(state->flag & PATH_RAY_MIS_SKIP)) {
/* multiple importance sampling, get regular light pdf,
* and compute weight with respect to BSDF pdf */
diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h
index ac432d3fe04..0adf9ed4666 100644
--- a/intern/cycles/kernel/kernel_light.h
+++ b/intern/cycles/kernel/kernel_light.h
@@ -208,8 +208,8 @@ ccl_device float lamp_light_pdf(KernelGlobals *kg, const float3 Ng, const float3
return t*t/cos_pi;
}
-ccl_device void lamp_light_sample(KernelGlobals *kg, int lamp,
- float randu, float randv, float3 P, LightSample *ls)
+ccl_device bool lamp_light_sample(KernelGlobals *kg, int lamp,
+ float randu, float randv, float3 P, LightSample *ls, bool for_volume)
{
float4 data0 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 0);
float4 data1 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 1);
@@ -224,6 +224,11 @@ ccl_device void lamp_light_sample(KernelGlobals *kg, int lamp,
ls->v = randv;
if(type == LIGHT_DISTANT) {
+#ifdef __VOLUME__
+ if(for_volume)
+ return false;
+#endif
+
/* distant light */
float3 lightD = make_float3(data0.y, data0.z, data0.w);
float3 D = lightD;
@@ -244,6 +249,11 @@ ccl_device void lamp_light_sample(KernelGlobals *kg, int lamp,
}
#ifdef __BACKGROUND_MIS__
else if(type == LIGHT_BACKGROUND) {
+#ifdef __VOLUME__
+ if(for_volume)
+ return false;
+#endif
+
/* infinite area light (e.g. light dome or env light) */
float3 D = background_light_sample(kg, randu, randv, &ls->pdf);
@@ -299,6 +309,8 @@ ccl_device void lamp_light_sample(KernelGlobals *kg, int lamp,
ls->eval_fac *= kernel_data.integrator.inv_pdf_lights;
ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t);
}
+
+ return true;
}
ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, float t, LightSample *ls)
@@ -514,7 +526,7 @@ ccl_device int light_distribution_sample(KernelGlobals *kg, float randt)
/* Generic Light */
-ccl_device void light_sample(KernelGlobals *kg, float randt, float randu, float randv, float time, float3 P, LightSample *ls)
+ccl_device bool light_sample(KernelGlobals *kg, float randt, float randu, float randv, float time, float3 P, LightSample *ls, bool for_volume)
{
/* sample index */
int index = light_distribution_sample(kg, randt);
@@ -533,10 +545,12 @@ ccl_device void light_sample(KernelGlobals *kg, float randt, float randu, float
ls->D = normalize_len(ls->P - P, &ls->t);
ls->pdf = triangle_light_pdf(kg, ls->Ng, -ls->D, ls->t);
ls->shader |= shader_flag;
+
+ return true;
}
else {
int lamp = -prim-1;
- lamp_light_sample(kg, lamp, randu, randv, P, ls);
+ return lamp_light_sample(kg, lamp, randu, randv, P, ls, for_volume);
}
}
@@ -546,9 +560,9 @@ ccl_device int light_select_num_samples(KernelGlobals *kg, int index)
return __float_as_int(data3.x);
}
-ccl_device void light_select(KernelGlobals *kg, int index, float randu, float randv, float3 P, LightSample *ls)
+ccl_device bool light_select(KernelGlobals *kg, int index, float randu, float randv, float3 P, LightSample *ls, bool for_volume)
{
- lamp_light_sample(kg, index, randu, randv, P, ls);
+ return lamp_light_sample(kg, index, randu, randv, P, ls, for_volume);
}
ccl_device int lamp_light_eval_sample(KernelGlobals *kg, float randt)
diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h
index b57e27400b9..83bceed44e5 100644
--- a/intern/cycles/kernel/kernel_path.h
+++ b/intern/cycles/kernel/kernel_path.h
@@ -29,7 +29,6 @@
#include "kernel_accumulate.h"
#include "kernel_shader.h"
#include "kernel_light.h"
-#include "kernel_emission.h"
#include "kernel_passes.h"
#ifdef __SUBSURFACE__
@@ -42,178 +41,12 @@
#include "kernel_path_state.h"
#include "kernel_shadow.h"
+#include "kernel_emission.h"
+#include "kernel_path_surface.h"
+#include "kernel_path_volume.h"
CCL_NAMESPACE_BEGIN
-#ifdef __VOLUME__
-
-ccl_device_inline bool kernel_path_integrate_scatter_lighting(KernelGlobals *kg, RNG *rng,
- ShaderData *sd, float3 *throughput, PathState *state, PathRadiance *L, Ray *ray,
- float num_samples_adjust)
-{
-#ifdef __EMISSION__
- if(kernel_data.integrator.use_direct_light) {
- /* sample illumination from lights to find path contribution */
- if(sd->flag & SD_BSDF_HAS_EVAL) {
- float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT);
- float light_u, light_v;
- path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
-
- Ray light_ray;
- BsdfEval L_light;
- bool is_lamp;
-
-#ifdef __OBJECT_MOTION__
- light_ray.time = sd->time;
-#endif
-
- if(direct_emission(kg, sd, LAMP_NONE, light_t, light_u, light_v, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
- /* trace shadow ray */
- float3 shadow;
-
- if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
- /* accumulate */
- path_radiance_accum_light(L, *throughput * num_samples_adjust, &L_light, shadow, 1.0f, state->bounce, is_lamp);
- }
- }
- }
- }
-#endif
-
- /* sample phase function */
- float phase_pdf;
- BsdfEval phase_eval;
- float3 phase_omega_in;
- differential3 phase_domega_in;
- float phase_u, phase_v;
- path_state_rng_2D(kg, rng, state, PRNG_PHASE_U, &phase_u, &phase_v);
- int label;
-
- label = shader_volume_phase_sample(kg, sd, phase_u, phase_v, &phase_eval,
- &phase_omega_in, &phase_domega_in, &phase_pdf);
-
- if(phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval))
- return false;
-
- /* modify throughput */
- path_radiance_bsdf_bounce(L, throughput, &phase_eval, phase_pdf, state->bounce, label);
-
- /* set labels */
- state->ray_pdf = phase_pdf;
-#ifdef __LAMP_MIS__
- state->ray_t = 0.0f;
-#endif
- state->min_ray_pdf = fminf(phase_pdf, state->min_ray_pdf);
-
- /* update path state */
- path_state_next(kg, state, label);
-
- /* setup ray */
- ray->P = sd->P;
- ray->D = phase_omega_in;
- ray->t = FLT_MAX;
-
-#ifdef __RAY_DIFFERENTIALS__
- ray->dP = sd->dP;
- ray->dD = phase_domega_in;
-#endif
-
- return true;
-}
-
-#endif
-
-#if defined(__BRANCHED_PATH__) || defined(__SUBSURFACE__)
-
-ccl_device void kernel_branched_path_integrate_direct_lighting(KernelGlobals *kg, RNG *rng,
- ShaderData *sd, PathState *state, float3 throughput, float num_samples_adjust, PathRadiance *L, bool sample_all_lights)
-{
- /* sample illumination from lights to find path contribution */
- if(sd->flag & SD_BSDF_HAS_EVAL) {
- Ray light_ray;
- BsdfEval L_light;
- bool is_lamp;
-
-#ifdef __OBJECT_MOTION__
- light_ray.time = sd->time;
-#endif
-
- if(sample_all_lights) {
- /* lamp sampling */
- for(int i = 0; i < kernel_data.integrator.num_all_lights; i++) {
- int num_samples = ceil_to_int(num_samples_adjust*light_select_num_samples(kg, i));
- float num_samples_inv = num_samples_adjust/(num_samples*kernel_data.integrator.num_all_lights);
- RNG lamp_rng = cmj_hash(*rng, i);
-
- if(kernel_data.integrator.pdf_triangles != 0.0f)
- num_samples_inv *= 0.5f;
-
- for(int j = 0; j < num_samples; j++) {
- float light_u, light_v;
- path_branched_rng_2D(kg, &lamp_rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
-
- if(direct_emission(kg, sd, i, 0.0f, light_u, light_v, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
- /* trace shadow ray */
- float3 shadow;
-
- if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
- /* accumulate */
- path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
- }
- }
- }
- }
-
- /* mesh light sampling */
- if(kernel_data.integrator.pdf_triangles != 0.0f) {
- int num_samples = ceil_to_int(num_samples_adjust*kernel_data.integrator.mesh_light_samples);
- float num_samples_inv = num_samples_adjust/num_samples;
-
- if(kernel_data.integrator.num_all_lights)
- num_samples_inv *= 0.5f;
-
- for(int j = 0; j < num_samples; j++) {
- float light_t = path_branched_rng_1D(kg, rng, state, j, num_samples, PRNG_LIGHT);
- float light_u, light_v;
- path_branched_rng_2D(kg, rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
-
- /* only sample triangle lights */
- if(kernel_data.integrator.num_all_lights)
- light_t = 0.5f*light_t;
-
- if(direct_emission(kg, sd, LAMP_NONE, light_t, light_u, light_v, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
- /* trace shadow ray */
- float3 shadow;
-
- if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
- /* accumulate */
- path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
- }
- }
- }
- }
- }
- else {
- float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT);
- float light_u, light_v;
- path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
-
- /* sample random light */
- if(direct_emission(kg, sd, LAMP_NONE, light_t, light_u, light_v, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
- /* trace shadow ray */
- float3 shadow;
-
- if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
- /* accumulate */
- path_radiance_accum_light(L, throughput*num_samples_adjust, &L_light, shadow, num_samples_adjust, state->bounce, is_lamp);
- }
- }
- }
- }
-}
-
-#endif
-
ccl_device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, Ray ray,
float3 throughput, int num_samples, PathState state, PathRadiance *L)
{
@@ -255,15 +88,79 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, Ray ray,
Ray volume_ray = ray;
volume_ray.t = (hit)? isect.t: FLT_MAX;
- ShaderData volume_sd;
- VolumeIntegrateResult result = kernel_volume_integrate(kg, &state,
- &volume_sd, &volume_ray, L, &throughput, rng);
+ bool heterogeneous = volume_stack_is_heterogeneous(kg, state.volume_stack);
+ int sampling_method = volume_stack_sampling_method(kg, state.volume_stack);
+ bool decoupled = kernel_volume_use_decoupled(kg, heterogeneous, false, sampling_method);
- if(result == VOLUME_PATH_SCATTERED) {
- if(kernel_path_integrate_scatter_lighting(kg, rng, &volume_sd, &throughput, &state, L, &ray, 1.0f))
- continue;
- else
- break;
+ if(decoupled) {
+ /* cache steps along volume for repeated sampling */
+ VolumeSegment volume_segment;
+ ShaderData volume_sd;
+
+ shader_setup_from_volume(kg, &volume_sd, &volume_ray, state.bounce, state.transparent_bounce);
+ kernel_volume_decoupled_record(kg, &state,
+ &volume_ray, &volume_sd, &volume_segment, heterogeneous);
+
+ volume_segment.sampling_method = sampling_method;
+
+ /* emission */
+ if(volume_segment.closure_flag & SD_EMISSION)
+ path_radiance_accum_emission(L, throughput, volume_segment.accum_emission, state.bounce);
+
+ /* scattering */
+ VolumeIntegrateResult result = VOLUME_PATH_ATTENUATED;
+ bool scatter = false;
+
+ if(volume_segment.closure_flag & SD_SCATTER) {
+ bool all = kernel_data.integrator.sample_all_lights_indirect;
+
+ /* direct light sampling */
+ kernel_branched_path_volume_connect_light(kg, rng, &volume_sd,
+ throughput, &state, L, 1.0f, all, &volume_ray, &volume_segment);
+
+ /* indirect sample. if we use distance sampling and take just
+ * one sample for direct and indirect light, we could share
+ * this computation, but makes code a bit complex */
+ float rphase = path_state_rng_1D_for_decision(kg, rng, &state, PRNG_PHASE);
+ float rscatter = path_state_rng_1D_for_decision(kg, rng, &state, PRNG_SCATTER_DISTANCE);
+
+ result = kernel_volume_decoupled_scatter(kg,
+ &state, &volume_ray, &volume_sd, &throughput,
+ rphase, rscatter, &volume_segment, NULL, true);
+
+ if(result == VOLUME_PATH_SCATTERED)
+ scatter = kernel_path_volume_bounce(kg, rng, &volume_sd, &throughput, &state, L, &ray, 1.0f);
+ }
+
+ if(result != VOLUME_PATH_SCATTERED)
+ throughput *= volume_segment.accum_transmittance;
+
+ /* free cached steps */
+ kernel_volume_decoupled_free(kg, &volume_segment);
+
+ if(result == VOLUME_PATH_SCATTERED) {
+ if(scatter)
+ continue;
+ else
+ break;
+ }
+ }
+ else {
+ /* integrate along volume segment with distance sampling */
+ ShaderData volume_sd;
+ VolumeIntegrateResult result = kernel_volume_integrate(
+ kg, &state, &volume_sd, &volume_ray, L, &throughput, rng);
+
+ if(result == VOLUME_PATH_SCATTERED) {
+ /* direct lighting */
+ kernel_path_volume_connect_light(kg, rng, &volume_sd, throughput, &state, L, 1.0f);
+
+ /* indirect light bounce */
+ if(kernel_path_volume_bounce(kg, rng, &volume_sd, &throughput, &state, L, &ray, 1.0f))
+ continue;
+ else
+ break;
+ }
}
}
#endif
@@ -383,187 +280,12 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, Ray ray,
#if defined(__EMISSION__) && defined(__BRANCHED_PATH__)
if(kernel_data.integrator.use_direct_light) {
bool all = kernel_data.integrator.sample_all_lights_indirect;
- kernel_branched_path_integrate_direct_lighting(kg, rng, &sd, &state, throughput, 1.0f, L, all);
+ kernel_branched_path_surface_connect_light(kg, rng, &sd, &state, throughput, 1.0f, L, all);
}
#endif
- /* no BSDF? we can stop here */
- if(sd.flag & SD_BSDF) {
- /* sample BSDF */
- float bsdf_pdf;
- BsdfEval bsdf_eval;
- float3 bsdf_omega_in;
- differential3 bsdf_domega_in;
- float bsdf_u, bsdf_v;
- path_state_rng_2D(kg, rng, &state, PRNG_BSDF_U, &bsdf_u, &bsdf_v);
- int label;
-
- label = shader_bsdf_sample(kg, &sd, bsdf_u, bsdf_v, &bsdf_eval,
- &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
-
- if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
- break;
-
- /* modify throughput */
- path_radiance_bsdf_bounce(L, &throughput, &bsdf_eval, bsdf_pdf, state.bounce, label);
-
- /* set labels */
- if(!(label & LABEL_TRANSPARENT)) {
- state.ray_pdf = bsdf_pdf;
-#ifdef __LAMP_MIS__
- state.ray_t = 0.0f;
-#endif
- state.min_ray_pdf = fminf(bsdf_pdf, state.min_ray_pdf);
- }
-
- /* update path state */
- path_state_next(kg, &state, label);
-
- /* setup ray */
- ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -sd.Ng: sd.Ng);
- ray.D = bsdf_omega_in;
- ray.t = FLT_MAX;
-#ifdef __RAY_DIFFERENTIALS__
- ray.dP = sd.dP;
- ray.dD = bsdf_domega_in;
-#endif
-
-#ifdef __VOLUME__
- /* enter/exit volume */
- if(label & LABEL_TRANSMIT)
- kernel_volume_stack_enter_exit(kg, &sd, state.volume_stack);
-#endif
- }
-#ifdef __VOLUME__
- else if(sd.flag & SD_HAS_ONLY_VOLUME) {
- /* no surface shader but have a volume shader? act transparent */
-
- /* update path state, count as transparent */
- path_state_next(kg, &state, LABEL_TRANSPARENT);
-
- /* setup ray position, direction stays unchanged */
- ray.P = ray_offset(sd.P, -sd.Ng);
-#ifdef __RAY_DIFFERENTIALS__
- ray.dP = sd.dP;
-#endif
-
- /* enter/exit volume */
- kernel_volume_stack_enter_exit(kg, &sd, state.volume_stack);
- }
-#endif
- else {
- /* no bsdf or volume? we're done */
+ if(!kernel_path_surface_bounce(kg, rng, &sd, &throughput, &state, L, &ray))
break;
- }
- }
-}
-
-ccl_device_inline bool kernel_path_integrate_lighting(KernelGlobals *kg, RNG *rng,
- ShaderData *sd, float3 *throughput, PathState *state, PathRadiance *L, Ray *ray)
-{
-#ifdef __EMISSION__
- if(kernel_data.integrator.use_direct_light) {
- /* sample illumination from lights to find path contribution */
- if(sd->flag & SD_BSDF_HAS_EVAL) {
- float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT);
- float light_u, light_v;
- path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
-
- Ray light_ray;
- BsdfEval L_light;
- bool is_lamp;
-
-#ifdef __OBJECT_MOTION__
- light_ray.time = sd->time;
-#endif
-
- if(direct_emission(kg, sd, LAMP_NONE, light_t, light_u, light_v, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
- /* trace shadow ray */
- float3 shadow;
-
- if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
- /* accumulate */
- path_radiance_accum_light(L, *throughput, &L_light, shadow, 1.0f, state->bounce, is_lamp);
- }
- }
- }
- }
-#endif
-
- /* no BSDF? we can stop here */
- if(sd->flag & SD_BSDF) {
- /* sample BSDF */
- float bsdf_pdf;
- BsdfEval bsdf_eval;
- float3 bsdf_omega_in;
- differential3 bsdf_domega_in;
- float bsdf_u, bsdf_v;
- path_state_rng_2D(kg, rng, state, PRNG_BSDF_U, &bsdf_u, &bsdf_v);
- int label;
-
- label = shader_bsdf_sample(kg, sd, bsdf_u, bsdf_v, &bsdf_eval,
- &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
-
- if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
- return false;
-
- /* modify throughput */
- path_radiance_bsdf_bounce(L, throughput, &bsdf_eval, bsdf_pdf, state->bounce, label);
-
- /* set labels */
- if(!(label & LABEL_TRANSPARENT)) {
- state->ray_pdf = bsdf_pdf;
-#ifdef __LAMP_MIS__
- state->ray_t = 0.0f;
-#endif
- state->min_ray_pdf = fminf(bsdf_pdf, state->min_ray_pdf);
- }
-
- /* update path state */
- path_state_next(kg, state, label);
-
- /* setup ray */
- ray->P = ray_offset(sd->P, (label & LABEL_TRANSMIT)? -sd->Ng: sd->Ng);
- ray->D = bsdf_omega_in;
-
- if(state->bounce == 0)
- ray->t -= sd->ray_length; /* clipping works through transparent */
- else
- ray->t = FLT_MAX;
-
-#ifdef __RAY_DIFFERENTIALS__
- ray->dP = sd->dP;
- ray->dD = bsdf_domega_in;
-#endif
-
-#ifdef __VOLUME__
- /* enter/exit volume */
- if(label & LABEL_TRANSMIT)
- kernel_volume_stack_enter_exit(kg, sd, state->volume_stack);
-#endif
- return true;
- }
-#ifdef __VOLUME__
- else if(sd->flag & SD_HAS_ONLY_VOLUME) {
- /* no surface shader but have a volume shader? act transparent */
-
- /* update path state, count as transparent */
- path_state_next(kg, state, LABEL_TRANSPARENT);
-
- /* setup ray position, direction stays unchanged */
- ray->P = ray_offset(sd->P, -sd->Ng);
-#ifdef __RAY_DIFFERENTIALS__
- ray->dP = sd->dP;
-#endif
-
- /* enter/exit volume */
- kernel_volume_stack_enter_exit(kg, sd, state->volume_stack);
- return true;
- }
-#endif
- else {
- /* no bsdf or volume? */
- return false;
}
}
@@ -664,8 +386,10 @@ ccl_device bool kernel_path_subsurface_scatter(KernelGlobals *kg, ShaderData *sd
hit_state.flag |= PATH_RAY_BSSRDF_ANCESTOR;
hit_state.rng_offset += PRNG_BOUNCE_NUM;
+
+ kernel_path_surface_connect_light(kg, rng, &bssrdf_sd[hit], tp, state, L);
- if(kernel_path_integrate_lighting(kg, rng, &bssrdf_sd[hit], &tp, &hit_state, L, &hit_ray)) {
+ if(kernel_path_surface_bounce(kg, rng, &bssrdf_sd[hit], &tp, &hit_state, L, &hit_ray)) {
#ifdef __LAMP_MIS__
hit_state.ray_t = 0.0f;
#endif
@@ -749,15 +473,79 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
Ray volume_ray = ray;
volume_ray.t = (hit)? isect.t: FLT_MAX;
- ShaderData volume_sd;
- VolumeIntegrateResult result = kernel_volume_integrate(kg, &state,
- &volume_sd, &volume_ray, &L, &throughput, rng);
+ bool heterogeneous = volume_stack_is_heterogeneous(kg, state.volume_stack);
+ int sampling_method = volume_stack_sampling_method(kg, state.volume_stack);
+ bool decoupled = kernel_volume_use_decoupled(kg, heterogeneous, true, sampling_method);
- if(result == VOLUME_PATH_SCATTERED) {
- if(kernel_path_integrate_scatter_lighting(kg, rng, &volume_sd, &throughput, &state, &L, &ray, 1.0f))
- continue;
- else
- break;
+ if(decoupled) {
+ /* cache steps along volume for repeated sampling */
+ VolumeSegment volume_segment;
+ ShaderData volume_sd;
+
+ shader_setup_from_volume(kg, &volume_sd, &volume_ray, state.bounce, state.transparent_bounce);
+ kernel_volume_decoupled_record(kg, &state,
+ &volume_ray, &volume_sd, &volume_segment, heterogeneous);
+
+ volume_segment.sampling_method = sampling_method;
+
+ /* emission */
+ if(volume_segment.closure_flag & SD_EMISSION)
+ path_radiance_accum_emission(&L, throughput, volume_segment.accum_emission, state.bounce);
+
+ /* scattering */
+ VolumeIntegrateResult result = VOLUME_PATH_ATTENUATED;
+ bool scatter = false;
+
+ if(volume_segment.closure_flag & SD_SCATTER) {
+ bool all = false;
+
+ /* direct light sampling */
+ kernel_branched_path_volume_connect_light(kg, rng, &volume_sd,
+ throughput, &state, &L, 1.0f, all, &volume_ray, &volume_segment);
+
+ /* indirect sample. if we use distance sampling and take just
+ * one sample for direct and indirect light, we could share
+ * this computation, but makes code a bit complex */
+ float rphase = path_state_rng_1D_for_decision(kg, rng, &state, PRNG_PHASE);
+ float rscatter = path_state_rng_1D_for_decision(kg, rng, &state, PRNG_SCATTER_DISTANCE);
+
+ result = kernel_volume_decoupled_scatter(kg,
+ &state, &volume_ray, &volume_sd, &throughput,
+ rphase, rscatter, &volume_segment, NULL, true);
+
+ if(result == VOLUME_PATH_SCATTERED)
+ scatter = kernel_path_volume_bounce(kg, rng, &volume_sd, &throughput, &state, &L, &ray, 1.0f);
+ }
+
+ if(result != VOLUME_PATH_SCATTERED)
+ throughput *= volume_segment.accum_transmittance;
+
+ /* free cached steps */
+ kernel_volume_decoupled_free(kg, &volume_segment);
+
+ if(result == VOLUME_PATH_SCATTERED) {
+ if(scatter)
+ continue;
+ else
+ break;
+ }
+ }
+ else {
+ /* integrate along volume segment with distance sampling */
+ ShaderData volume_sd;
+ VolumeIntegrateResult result = kernel_volume_integrate(
+ kg, &state, &volume_sd, &volume_ray, &L, &throughput, rng);
+
+ if(result == VOLUME_PATH_SCATTERED) {
+ /* direct lighting */
+ kernel_path_volume_connect_light(kg, rng, &volume_sd, throughput, &state, &L, 1.0f);
+
+ /* indirect light bounce */
+ if(kernel_path_volume_bounce(kg, rng, &volume_sd, &throughput, &state, &L, &ray, 1.0f))
+ continue;
+ else
+ break;
+ }
}
}
#endif
@@ -863,112 +651,13 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
break;
}
#endif
-
- /* Same as kernel_path_integrate_lighting(kg, rng, &sd, &throughput, &state, &L, &ray),
- but for CUDA the function call is slower. */
-#ifdef __EMISSION__
- if(kernel_data.integrator.use_direct_light) {
- /* sample illumination from lights to find path contribution */
- if(sd.flag & SD_BSDF_HAS_EVAL) {
- float light_t = path_state_rng_1D(kg, rng, &state, PRNG_LIGHT);
- float light_u, light_v;
- path_state_rng_2D(kg, rng, &state, PRNG_LIGHT_U, &light_u, &light_v);
-
- Ray light_ray;
- BsdfEval L_light;
- bool is_lamp;
-
-#ifdef __OBJECT_MOTION__
- light_ray.time = sd.time;
-#endif
-
- if(direct_emission(kg, &sd, LAMP_NONE, light_t, light_u, light_v, &light_ray, &L_light, &is_lamp, state.bounce, state.transparent_bounce)) {
- /* trace shadow ray */
- float3 shadow;
-
- if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
- /* accumulate */
- path_radiance_accum_light(&L, throughput, &L_light, shadow, 1.0f, state.bounce, is_lamp);
- }
- }
- }
- }
-#endif
- if(sd.flag & SD_BSDF) {
- /* sample BSDF */
- float bsdf_pdf;
- BsdfEval bsdf_eval;
- float3 bsdf_omega_in;
- differential3 bsdf_domega_in;
- float bsdf_u, bsdf_v;
- path_state_rng_2D(kg, rng, &state, PRNG_BSDF_U, &bsdf_u, &bsdf_v);
- int label;
-
- label = shader_bsdf_sample(kg, &sd, bsdf_u, bsdf_v, &bsdf_eval,
- &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
-
- if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
- break;
+ /* direct lighting */
+ kernel_path_surface_connect_light(kg, rng, &sd, throughput, &state, &L);
- /* modify throughput */
- path_radiance_bsdf_bounce(&L, &throughput, &bsdf_eval, bsdf_pdf, state.bounce, label);
-
- /* set labels */
- if(!(label & LABEL_TRANSPARENT)) {
- state.ray_pdf = bsdf_pdf;
-#ifdef __LAMP_MIS__
- state.ray_t = 0.0f;
-#endif
- state.min_ray_pdf = fminf(bsdf_pdf, state.min_ray_pdf);
- }
-
- /* update path state */
- path_state_next(kg, &state, label);
-
- /* setup ray */
- ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -sd.Ng: sd.Ng);
- ray.D = bsdf_omega_in;
-
-#ifdef __RAY_DIFFERENTIALS__
- ray.dP = sd.dP;
- ray.dD = bsdf_domega_in;
-#endif
-
-#ifdef __VOLUME__
- /* enter/exit volume */
- if(label & LABEL_TRANSMIT)
- kernel_volume_stack_enter_exit(kg, &sd, state.volume_stack);
-#endif
-
- }
-#ifdef __VOLUME__
- else if(sd.flag & SD_HAS_ONLY_VOLUME) {
- /* no surface shader but have a volume shader? act transparent */
-
- /* update path state, count as transparent */
- path_state_next(kg, &state, LABEL_TRANSPARENT);
-
- /* setup ray position, direction stays unchanged */
- ray.P = ray_offset(sd.P, -sd.Ng);
-#ifdef __RAY_DIFFERENTIALS__
- ray.dP = sd.dP;
-#endif
-
- /* enter/exit volume */
- kernel_volume_stack_enter_exit(kg, &sd, state.volume_stack);
- }
-#endif
- else {
- /* no bsdf or volume? we're done */
+ /* compute direct lighting and next bounce */
+ if(!kernel_path_surface_bounce(kg, rng, &sd, &throughput, &state, &L, &ray))
break;
- }
-
- /* adjust ray distance for clipping */
- if(state.bounce == 0)
- ray.t -= sd.ray_length; /* clipping works through transparent */
- else
- ray.t = FLT_MAX;
}
float3 L_sum = path_radiance_clamp_and_sum(kg, &L);
@@ -980,17 +669,11 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
#ifdef __BRANCHED_PATH__
-ccl_device_noinline void kernel_branched_path_integrate_lighting(KernelGlobals *kg,
+/* branched path tracing: bounce off surface and integrate indirect light */
+ccl_device_noinline void kernel_branched_path_surface_indirect_light(KernelGlobals *kg,
RNG *rng, ShaderData *sd, float3 throughput, float num_samples_adjust,
PathState *state, PathRadiance *L)
{
-#ifdef __EMISSION__
- if(kernel_data.integrator.use_direct_light) {
- bool all = kernel_data.integrator.sample_all_lights_direct;
- kernel_branched_path_integrate_direct_lighting(kg, rng, sd, state, throughput, num_samples_adjust, L, all);
- }
-#endif
-
for(int i = 0; i< sd->num_closure; i++) {
const ShaderClosure *sc = &sd->closure[i];
@@ -1017,58 +700,12 @@ ccl_device_noinline void kernel_branched_path_integrate_lighting(KernelGlobals *
RNG bsdf_rng = cmj_hash(*rng, i);
for(int j = 0; j < num_samples; j++) {
- /* sample BSDF */
- float bsdf_pdf;
- BsdfEval bsdf_eval;
- float3 bsdf_omega_in;
- differential3 bsdf_domega_in;
- float bsdf_u, bsdf_v;
- path_branched_rng_2D(kg, &bsdf_rng, state, j, num_samples, PRNG_BSDF_U, &bsdf_u, &bsdf_v);
- int label;
-
- label = shader_bsdf_sample_closure(kg, sd, sc, bsdf_u, bsdf_v, &bsdf_eval,
- &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
-
- if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
- continue;
-
- /* modify throughput */
- float3 tp = throughput;
- path_radiance_bsdf_bounce(L, &tp, &bsdf_eval, bsdf_pdf, state->bounce, label);
-
- /* modify path state */
PathState ps = *state;
- path_state_next(kg, &ps, label);
-
- /* setup ray */
+ float3 tp = throughput;
Ray bsdf_ray;
- bsdf_ray.P = ray_offset(sd->P, (label & LABEL_TRANSMIT)? -sd->Ng: sd->Ng);
- bsdf_ray.D = bsdf_omega_in;
- bsdf_ray.t = FLT_MAX;
-#ifdef __RAY_DIFFERENTIALS__
- bsdf_ray.dP = sd->dP;
- bsdf_ray.dD = bsdf_domega_in;
-#endif
-#ifdef __OBJECT_MOTION__
- bsdf_ray.time = sd->time;
-#endif
-
-#ifdef __VOLUME__
- /* enter/exit volume */
- if(label & LABEL_TRANSMIT)
- kernel_volume_stack_enter_exit(kg, sd, ps.volume_stack);
-#endif
-
- /* branch RNG state */
- path_state_branch(&ps, j, num_samples);
-
- /* set MIS state */
- ps.min_ray_pdf = fminf(bsdf_pdf, FLT_MAX);
- ps.ray_pdf = bsdf_pdf;
-#ifdef __LAMP_MIS__
- ps.ray_t = 0.0f;
-#endif
+ if(!kernel_branched_path_surface_bounce(kg, &bsdf_rng, sd, sc, j, num_samples, &tp, &ps, L, &bsdf_ray))
+ continue;
kernel_path_indirect(kg, rng, bsdf_ray, tp*num_samples_inv, num_samples, ps, L);
@@ -1111,15 +748,24 @@ ccl_device void kernel_branched_path_subsurface_scatter(KernelGlobals *kg, Shade
path_state_branch(&hit_state, j, num_samples);
- kernel_branched_path_integrate_lighting(kg, rng,
- &bssrdf_sd[hit], throughput, num_samples_inv,
- &hit_state, L);
+#if defined(__EMISSION__) && defined(__BRANCHED_PATH__)
+ /* direct light */
+ if(kernel_data.integrator.use_direct_light) {
+ bool all = kernel_data.integrator.sample_all_lights_direct;
+ kernel_branched_path_surface_connect_light(kg, rng,
+ &bssrdf_sd[hit], &hit_state, throughput, num_samples_inv, L, all);
+ }
+#endif
+
+ /* indirect light */
+ kernel_branched_path_surface_indirect_light(kg, rng,
+ &bssrdf_sd[hit], throughput, num_samples_inv,
+ &hit_state, L);
}
}
state->flag &= ~PATH_RAY_BSSRDF_ANCESTOR;
}
-
}
#endif
@@ -1178,35 +824,50 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
kernel_volume_decoupled_record(kg, &state,
&volume_ray, &volume_sd, &volume_segment, heterogeneous);
- /* sample scattering */
- int num_samples = kernel_data.integrator.volume_samples;
- float num_samples_inv = 1.0f/num_samples;
+ /* direct light sampling */
+ if(volume_segment.closure_flag & SD_SCATTER) {
+ volume_segment.sampling_method = volume_stack_sampling_method(kg, state.volume_stack);
- for(int j = 0; j < num_samples; j++) {
- /* workaround to fix correlation bug in T38710, can find better solution
- * in random number generator later, for now this is done here to not impact
- * performance of rendering without volumes */
- RNG tmp_rng = cmj_hash(*rng, state.rng_offset);
+ bool all = kernel_data.integrator.sample_all_lights_direct;
- PathState ps = state;
- Ray pray = ray;
- float3 tp = throughput;
+ kernel_branched_path_volume_connect_light(kg, rng, &volume_sd,
+ throughput, &state, &L, 1.0f, all, &volume_ray, &volume_segment);
- /* branch RNG state */
- path_state_branch(&ps, j, num_samples);
+ /* indirect light sampling */
+ int num_samples = kernel_data.integrator.volume_samples;
+ float num_samples_inv = 1.0f/num_samples;
- VolumeIntegrateResult result = kernel_volume_decoupled_scatter(kg,
- &ps, &volume_ray, &volume_sd, &tp, &tmp_rng, &volume_segment);
-
- if(result == VOLUME_PATH_SCATTERED) {
- /* todo: use all-light sampling */
- if(kernel_path_integrate_scatter_lighting(kg, rng, &volume_sd, &tp, &ps, &L, &pray, num_samples_inv)) {
- kernel_path_indirect(kg, rng, pray, tp*num_samples_inv, num_samples, ps, &L);
-
- /* for render passes, sum and reset indirect light pass variables
- * for the next samples */
- path_radiance_sum_indirect(&L);
- path_radiance_reset_indirect(&L);
+ for(int j = 0; j < num_samples; j++) {
+ /* workaround to fix correlation bug in T38710, can find better solution
+ * in random number generator later, for now this is done here to not impact
+ * performance of rendering without volumes */
+ RNG tmp_rng = cmj_hash(*rng, state.rng_offset);
+
+ PathState ps = state;
+ Ray pray = ray;
+ float3 tp = throughput;
+
+ /* branch RNG state */
+ path_state_branch(&ps, j, num_samples);
+
+ /* scatter sample. if we use distance sampling and take just one
+ * sample for direct and indirect light, we could share this
+ * computation, but makes code a bit complex */
+ float rphase = path_state_rng_1D_for_decision(kg, &tmp_rng, &ps, PRNG_PHASE);
+ float rscatter = path_state_rng_1D_for_decision(kg, &tmp_rng, &ps, PRNG_SCATTER_DISTANCE);
+
+ VolumeIntegrateResult result = kernel_volume_decoupled_scatter(kg,
+ &ps, &pray, &volume_sd, &tp, rphase, rscatter, &volume_segment, NULL, false);
+
+ if(result == VOLUME_PATH_SCATTERED) {
+ if(kernel_path_volume_bounce(kg, rng, &volume_sd, &tp, &ps, &L, &pray, num_samples_inv)) {
+ kernel_path_indirect(kg, rng, pray, tp*num_samples_inv, num_samples, ps, &L);
+
+ /* for render passes, sum and reset indirect light pass variables
+ * for the next samples */
+ path_radiance_sum_indirect(&L);
+ path_radiance_reset_indirect(&L);
+ }
}
}
}
@@ -1235,12 +896,15 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
/* branch RNG state */
path_state_branch(&ps, j, num_samples);
- VolumeIntegrateResult result = kernel_volume_integrate(kg, &ps,
- &volume_sd, &volume_ray, &L, &tp, rng);
+ VolumeIntegrateResult result = kernel_volume_integrate(
+ kg, &ps, &volume_sd, &volume_ray, &L, &tp, rng);
if(result == VOLUME_PATH_SCATTERED) {
- /* todo: use all-light sampling */
- if(kernel_path_integrate_scatter_lighting(kg, rng, &volume_sd, &tp, &ps, &L, &pray, num_samples_inv)) {
+ /* todo: support equiangular, MIS and all light sampling.
+ * alternatively get decoupled ray marching working on the GPU */
+ kernel_path_volume_connect_light(kg, rng, &volume_sd, &volume_ray, throughput, &state, &L, num_samples_inv);
+
+ if(kernel_path_volume_bounce(kg, rng, &volume_sd, &tp, &ps, &L, &pray, num_samples_inv)) {
kernel_path_indirect(kg, rng, pray, tp*num_samples_inv, num_samples, ps, &L);
/* for render passes, sum and reset indirect light pass variables
@@ -1351,8 +1015,17 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
if(!(sd.flag & SD_HAS_ONLY_VOLUME)) {
PathState hit_state = state;
- /* lighting */
- kernel_branched_path_integrate_lighting(kg, rng,
+#ifdef __EMISSION__
+ /* direct light */
+ if(kernel_data.integrator.use_direct_light) {
+ bool all = kernel_data.integrator.sample_all_lights_direct;
+ kernel_branched_path_surface_connect_light(kg, rng,
+ &sd, &hit_state, throughput, 1.0f, &L, all);
+ }
+#endif
+
+ /* indirect light */
+ kernel_branched_path_surface_indirect_light(kg, rng,
&sd, throughput, 1.0f, &hit_state, &L);
/* continue in case of transparency */
diff --git a/intern/cycles/kernel/kernel_path_state.h b/intern/cycles/kernel/kernel_path_state.h
index 39780c0edb0..fc51c614c71 100644
--- a/intern/cycles/kernel/kernel_path_state.h
+++ b/intern/cycles/kernel/kernel_path_state.h
@@ -18,7 +18,7 @@ CCL_NAMESPACE_BEGIN
ccl_device_inline void path_state_init(KernelGlobals *kg, PathState *state, RNG *rng, int sample)
{
- state->flag = PATH_RAY_CAMERA|PATH_RAY_SINGULAR|PATH_RAY_MIS_SKIP;
+ state->flag = PATH_RAY_CAMERA|PATH_RAY_MIS_SKIP;
state->rng_offset = PRNG_BASE_NUM;
state->sample = sample;
diff --git a/intern/cycles/kernel/kernel_path_surface.h b/intern/cycles/kernel/kernel_path_surface.h
new file mode 100644
index 00000000000..7700abfbdae
--- /dev/null
+++ b/intern/cycles/kernel/kernel_path_surface.h
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * 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
+ */
+
+CCL_NAMESPACE_BEGIN
+
+#if defined(__BRANCHED_PATH__) || defined(__SUBSURFACE__)
+
+/* branched path tracing: connect path directly to position on one or more lights and add it to L */
+ccl_device void kernel_branched_path_surface_connect_light(KernelGlobals *kg, RNG *rng,
+ ShaderData *sd, PathState *state, float3 throughput, float num_samples_adjust, PathRadiance *L, bool sample_all_lights)
+{
+#ifdef __EMISSION__
+ /* sample illumination from lights to find path contribution */
+ if(!(sd->flag & SD_BSDF_HAS_EVAL))
+ return;
+
+ Ray light_ray;
+ BsdfEval L_light;
+ bool is_lamp;
+
+#ifdef __OBJECT_MOTION__
+ light_ray.time = sd->time;
+#endif
+
+ if(sample_all_lights) {
+ /* lamp sampling */
+ for(int i = 0; i < kernel_data.integrator.num_all_lights; i++) {
+ int num_samples = ceil_to_int(num_samples_adjust*light_select_num_samples(kg, i));
+ float num_samples_inv = num_samples_adjust/(num_samples*kernel_data.integrator.num_all_lights);
+ RNG lamp_rng = cmj_hash(*rng, i);
+
+ if(kernel_data.integrator.pdf_triangles != 0.0f)
+ num_samples_inv *= 0.5f;
+
+ for(int j = 0; j < num_samples; j++) {
+ float light_u, light_v;
+ path_branched_rng_2D(kg, &lamp_rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
+
+ LightSample ls;
+ light_select(kg, i, light_u, light_v, sd->P, &ls, false);
+
+ if(direct_emission(kg, sd, &ls, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
+ /* trace shadow ray */
+ float3 shadow;
+
+ if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
+ /* accumulate */
+ path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
+ }
+ }
+ }
+ }
+
+ /* mesh light sampling */
+ if(kernel_data.integrator.pdf_triangles != 0.0f) {
+ int num_samples = ceil_to_int(num_samples_adjust*kernel_data.integrator.mesh_light_samples);
+ float num_samples_inv = num_samples_adjust/num_samples;
+
+ if(kernel_data.integrator.num_all_lights)
+ num_samples_inv *= 0.5f;
+
+ for(int j = 0; j < num_samples; j++) {
+ float light_t = path_branched_rng_1D(kg, rng, state, j, num_samples, PRNG_LIGHT);
+ float light_u, light_v;
+ path_branched_rng_2D(kg, rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
+
+ /* only sample triangle lights */
+ if(kernel_data.integrator.num_all_lights)
+ light_t = 0.5f*light_t;
+
+ LightSample ls;
+ light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, &ls, false);
+
+ if(direct_emission(kg, sd, &ls, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
+ /* trace shadow ray */
+ float3 shadow;
+
+ if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
+ /* accumulate */
+ path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
+ }
+ }
+ }
+ }
+ }
+ else {
+ /* sample one light at random */
+ float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT);
+ float light_u, light_v;
+ path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
+
+ LightSample ls;
+ light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, &ls, false);
+
+ /* sample random light */
+ if(direct_emission(kg, sd, &ls, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
+ /* trace shadow ray */
+ float3 shadow;
+
+ if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
+ /* accumulate */
+ path_radiance_accum_light(L, throughput*num_samples_adjust, &L_light, shadow, num_samples_adjust, state->bounce, is_lamp);
+ }
+ }
+ }
+#endif
+}
+
+/* branched path tracing: bounce off or through surface to with new direction stored in ray */
+ccl_device bool kernel_branched_path_surface_bounce(KernelGlobals *kg, RNG *rng,
+ ShaderData *sd, const ShaderClosure *sc, int sample, int num_samples,
+ float3 *throughput, PathState *state, PathRadiance *L, Ray *ray)
+{
+ /* sample BSDF */
+ float bsdf_pdf;
+ BsdfEval bsdf_eval;
+ float3 bsdf_omega_in;
+ differential3 bsdf_domega_in;
+ float bsdf_u, bsdf_v;
+ path_branched_rng_2D(kg, rng, state, sample, num_samples, PRNG_BSDF_U, &bsdf_u, &bsdf_v);
+ int label;
+
+ label = shader_bsdf_sample_closure(kg, sd, sc, bsdf_u, bsdf_v, &bsdf_eval,
+ &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
+
+ if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
+ return false;
+
+ /* modify throughput */
+ path_radiance_bsdf_bounce(L, throughput, &bsdf_eval, bsdf_pdf, state->bounce, label);
+
+ /* modify path state */
+ path_state_next(kg, state, label);
+
+ /* setup ray */
+ ray->P = ray_offset(sd->P, (label & LABEL_TRANSMIT)? -sd->Ng: sd->Ng);
+ ray->D = bsdf_omega_in;
+ ray->t = FLT_MAX;
+#ifdef __RAY_DIFFERENTIALS__
+ ray->dP = sd->dP;
+ ray->dD = bsdf_domega_in;
+#endif
+#ifdef __OBJECT_MOTION__
+ ray->time = sd->time;
+#endif
+
+#ifdef __VOLUME__
+ /* enter/exit volume */
+ if(label & LABEL_TRANSMIT)
+ kernel_volume_stack_enter_exit(kg, sd, state->volume_stack);
+#endif
+
+ /* branch RNG state */
+ path_state_branch(state, sample, num_samples);
+
+ /* set MIS state */
+ state->min_ray_pdf = fminf(bsdf_pdf, FLT_MAX);
+ state->ray_pdf = bsdf_pdf;
+#ifdef __LAMP_MIS__
+ state->ray_t = 0.0f;
+#endif
+
+ return true;
+}
+
+#endif
+
+/* path tracing: connect path directly to position on a light and add it to L */
+ccl_device_inline void kernel_path_surface_connect_light(KernelGlobals *kg, RNG *rng,
+ ShaderData *sd, float3 throughput, PathState *state, PathRadiance *L)
+{
+#ifdef __EMISSION__
+ if(!(kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL)))
+ return;
+
+ /* sample illumination from lights to find path contribution */
+ float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT);
+ float light_u, light_v;
+ path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
+
+ Ray light_ray;
+ BsdfEval L_light;
+ bool is_lamp;
+
+#ifdef __OBJECT_MOTION__
+ light_ray.time = sd->time;
+#endif
+
+ LightSample ls;
+ light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, &ls, false);
+
+ if(direct_emission(kg, sd, &ls, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
+ /* trace shadow ray */
+ float3 shadow;
+
+ if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
+ /* accumulate */
+ path_radiance_accum_light(L, throughput, &L_light, shadow, 1.0f, state->bounce, is_lamp);
+ }
+ }
+#endif
+}
+
+/* path tracing: bounce off or through surface to with new direction stored in ray */
+ccl_device_inline bool kernel_path_surface_bounce(KernelGlobals *kg, RNG *rng,
+ ShaderData *sd, float3 *throughput, PathState *state, PathRadiance *L, Ray *ray)
+{
+ /* no BSDF? we can stop here */
+ if(sd->flag & SD_BSDF) {
+ /* sample BSDF */
+ float bsdf_pdf;
+ BsdfEval bsdf_eval;
+ float3 bsdf_omega_in;
+ differential3 bsdf_domega_in;
+ float bsdf_u, bsdf_v;
+ path_state_rng_2D(kg, rng, state, PRNG_BSDF_U, &bsdf_u, &bsdf_v);
+ int label;
+
+ label = shader_bsdf_sample(kg, sd, bsdf_u, bsdf_v, &bsdf_eval,
+ &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
+
+ if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
+ return false;
+
+ /* modify throughput */
+ path_radiance_bsdf_bounce(L, throughput, &bsdf_eval, bsdf_pdf, state->bounce, label);
+
+ /* set labels */
+ if(!(label & LABEL_TRANSPARENT)) {
+ state->ray_pdf = bsdf_pdf;
+#ifdef __LAMP_MIS__
+ state->ray_t = 0.0f;
+#endif
+ state->min_ray_pdf = fminf(bsdf_pdf, state->min_ray_pdf);
+ }
+
+ /* update path state */
+ path_state_next(kg, state, label);
+
+ /* setup ray */
+ ray->P = ray_offset(sd->P, (label & LABEL_TRANSMIT)? -sd->Ng: sd->Ng);
+ ray->D = bsdf_omega_in;
+
+ if(state->bounce == 0)
+ ray->t -= sd->ray_length; /* clipping works through transparent */
+ else
+ ray->t = FLT_MAX;
+
+#ifdef __RAY_DIFFERENTIALS__
+ ray->dP = sd->dP;
+ ray->dD = bsdf_domega_in;
+#endif
+
+#ifdef __VOLUME__
+ /* enter/exit volume */
+ if(label & LABEL_TRANSMIT)
+ kernel_volume_stack_enter_exit(kg, sd, state->volume_stack);
+#endif
+ return true;
+ }
+#ifdef __VOLUME__
+ else if(sd->flag & SD_HAS_ONLY_VOLUME) {
+ /* no surface shader but have a volume shader? act transparent */
+
+ /* update path state, count as transparent */
+ path_state_next(kg, state, LABEL_TRANSPARENT);
+
+ /* setup ray position, direction stays unchanged */
+ ray->P = ray_offset(sd->P, -sd->Ng);
+#ifdef __RAY_DIFFERENTIALS__
+ ray->dP = sd->dP;
+#endif
+
+ /* enter/exit volume */
+ kernel_volume_stack_enter_exit(kg, sd, state->volume_stack);
+ return true;
+ }
+#endif
+ else {
+ /* no bsdf or volume? */
+ return false;
+ }
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/kernel/kernel_path_volume.h b/intern/cycles/kernel/kernel_path_volume.h
new file mode 100644
index 00000000000..6cc3085b96e
--- /dev/null
+++ b/intern/cycles/kernel/kernel_path_volume.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * 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
+ */
+
+CCL_NAMESPACE_BEGIN
+
+#ifdef __VOLUME__
+
+ccl_device void kernel_path_volume_connect_light(KernelGlobals *kg, RNG *rng,
+ ShaderData *sd, float3 throughput, PathState *state, PathRadiance *L,
+ float num_samples_adjust)
+{
+#ifdef __EMISSION__
+ if(!kernel_data.integrator.use_direct_light)
+ return;
+
+ /* sample illumination from lights to find path contribution */
+ float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT);
+ float light_u, light_v;
+ path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
+
+ Ray light_ray;
+ BsdfEval L_light;
+ LightSample ls;
+ bool is_lamp;
+
+ /* connect to light from given point where shader has been evaluated */
+#ifdef __OBJECT_MOTION__
+ light_ray.time = sd->time;
+#endif
+
+ if(!light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, &ls, true))
+ return;
+ else if(ls.pdf == 0.0f)
+ return;
+
+ if(direct_emission(kg, sd, &ls, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
+ /* trace shadow ray */
+ float3 shadow;
+
+ if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
+ /* accumulate */
+ path_radiance_accum_light(L, throughput * num_samples_adjust, &L_light, shadow, 1.0f, state->bounce, is_lamp);
+ }
+ }
+#endif
+}
+
+ccl_device bool kernel_path_volume_bounce(KernelGlobals *kg, RNG *rng,
+ ShaderData *sd, float3 *throughput, PathState *state, PathRadiance *L, Ray *ray,
+ float num_samples_adjust)
+{
+ /* sample phase function */
+ float phase_pdf;
+ BsdfEval phase_eval;
+ float3 phase_omega_in;
+ differential3 phase_domega_in;
+ float phase_u, phase_v;
+ path_state_rng_2D(kg, rng, state, PRNG_PHASE_U, &phase_u, &phase_v);
+ int label;
+
+ label = shader_volume_phase_sample(kg, sd, phase_u, phase_v, &phase_eval,
+ &phase_omega_in, &phase_domega_in, &phase_pdf);
+
+ if(phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval))
+ return false;
+
+ /* modify throughput */
+ path_radiance_bsdf_bounce(L, throughput, &phase_eval, phase_pdf, state->bounce, label);
+
+ /* set labels */
+ state->ray_pdf = phase_pdf;
+#ifdef __LAMP_MIS__
+ state->ray_t = 0.0f;
+#endif
+ state->min_ray_pdf = fminf(phase_pdf, state->min_ray_pdf);
+
+ /* update path state */
+ path_state_next(kg, state, label);
+
+ /* setup ray */
+ ray->P = sd->P;
+ ray->D = phase_omega_in;
+ ray->t = FLT_MAX;
+
+#ifdef __RAY_DIFFERENTIALS__
+ ray->dP = sd->dP;
+ ray->dD = phase_domega_in;
+#endif
+
+ return true;
+}
+
+#ifdef __KERNEL_CPU__
+
+ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG *rng,
+ ShaderData *sd, float3 throughput, PathState *state, PathRadiance *L,
+ float num_samples_adjust, bool sample_all_lights, Ray *ray, const VolumeSegment *segment)
+{
+#ifdef __EMISSION__
+ if(!kernel_data.integrator.use_direct_light)
+ return;
+
+ Ray light_ray;
+ BsdfEval L_light;
+ bool is_lamp;
+
+#ifdef __OBJECT_MOTION__
+ light_ray.time = sd->time;
+#endif
+
+ if(sample_all_lights) {
+ /* lamp sampling */
+ for(int i = 0; i < kernel_data.integrator.num_all_lights; i++) {
+ int num_samples = ceil_to_int(num_samples_adjust*light_select_num_samples(kg, i));
+ float num_samples_inv = num_samples_adjust/(num_samples*kernel_data.integrator.num_all_lights);
+ RNG lamp_rng = cmj_hash(*rng, i);
+
+ if(kernel_data.integrator.pdf_triangles != 0.0f)
+ num_samples_inv *= 0.5f;
+
+ for(int j = 0; j < num_samples; j++) {
+ /* sample random position on given light */
+ float light_u, light_v;
+ path_branched_rng_2D(kg, &lamp_rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
+
+ LightSample ls;
+ if(!light_select(kg, i, light_u, light_v, ray->P, &ls, true))
+ continue;
+
+ float3 tp = throughput;
+
+ /* sample position on volume segment */
+ if(segment) {
+ float rphase = path_branched_rng_1D_for_decision(kg, rng, state, j, num_samples, PRNG_PHASE);
+ float rscatter = path_branched_rng_1D_for_decision(kg, rng, state, j, num_samples, PRNG_SCATTER_DISTANCE);
+
+ VolumeIntegrateResult result = kernel_volume_decoupled_scatter(kg,
+ state, ray, sd, &tp, rphase, rscatter, segment, (ls.t != FLT_MAX)? &ls.P: NULL, false);
+
+ if(result != VOLUME_PATH_SCATTERED)
+ continue;
+
+ /* todo: split up light_sample so we don't have to call it again with new position */
+ if(!light_select(kg, i, light_u, light_v, sd->P, &ls, true))
+ continue;
+ }
+
+ if(ls.pdf == 0.0f)
+ continue;
+
+ if(direct_emission(kg, sd, &ls, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
+ /* trace shadow ray */
+ float3 shadow;
+
+ if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
+ /* accumulate */
+ path_radiance_accum_light(L, tp*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
+ }
+ }
+ }
+ }
+
+ /* mesh light sampling */
+ if(kernel_data.integrator.pdf_triangles != 0.0f) {
+ int num_samples = ceil_to_int(num_samples_adjust*kernel_data.integrator.mesh_light_samples);
+ float num_samples_inv = num_samples_adjust/num_samples;
+
+ if(kernel_data.integrator.num_all_lights)
+ num_samples_inv *= 0.5f;
+
+ for(int j = 0; j < num_samples; j++) {
+ /* sample random position on random triangle */
+ float light_t = path_branched_rng_1D_for_decision(kg, rng, state, j, num_samples, PRNG_LIGHT);
+ float light_u, light_v;
+ path_branched_rng_2D(kg, rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
+
+ /* only sample triangle lights */
+ if(kernel_data.integrator.num_all_lights)
+ light_t = 0.5f*light_t;
+
+ LightSample ls;
+ if(!light_sample(kg, light_t, light_u, light_v, sd->time, ray->P, &ls, true))
+ continue;
+
+ float3 tp = throughput;
+
+ /* sample position on volume segment */
+ if(segment) {
+ float rphase = path_branched_rng_1D_for_decision(kg, rng, state, j, num_samples, PRNG_PHASE);
+ float rscatter = path_branched_rng_1D_for_decision(kg, rng, state, j, num_samples, PRNG_SCATTER_DISTANCE);
+
+ VolumeIntegrateResult result = kernel_volume_decoupled_scatter(kg,
+ state, ray, sd, &tp, rphase, rscatter, segment, (ls.t != FLT_MAX)? &ls.P: NULL, false);
+
+ if(result != VOLUME_PATH_SCATTERED)
+ continue;
+
+ /* todo: split up light_sample so we don't have to call it again with new position */
+ if(!light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, &ls, true))
+ continue;
+ }
+
+ if(ls.pdf == 0.0f)
+ continue;
+
+ if(direct_emission(kg, sd, &ls, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
+ /* trace shadow ray */
+ float3 shadow;
+
+ if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
+ /* accumulate */
+ path_radiance_accum_light(L, tp*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
+ }
+ }
+ }
+ }
+ }
+ else {
+ /* sample random position on random light */
+ float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT);
+ float light_u, light_v;
+ path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
+
+ LightSample ls;
+ if(!light_sample(kg, light_t, light_u, light_v, sd->time, ray->P, &ls, true))
+ return;
+
+ float3 tp = throughput;
+
+ /* sample position on volume segment */
+ if(segment) {
+ float rphase = path_state_rng_1D_for_decision(kg, rng, state, PRNG_PHASE);
+ float rscatter = path_state_rng_1D_for_decision(kg, rng, state, PRNG_SCATTER_DISTANCE);
+
+ VolumeIntegrateResult result = kernel_volume_decoupled_scatter(kg,
+ state, ray, sd, &tp, rphase, rscatter, segment, (ls.t != FLT_MAX)? &ls.P: NULL, false);
+
+ if(result != VOLUME_PATH_SCATTERED)
+ return;
+
+ /* todo: split up light_sample so we don't have to call it again with new position */
+ if(!light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, &ls, true))
+ return;
+ }
+
+ if(ls.pdf == 0.0f)
+ return;
+
+ /* sample random light */
+ if(direct_emission(kg, sd, &ls, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
+ /* trace shadow ray */
+ float3 shadow;
+
+ if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
+ /* accumulate */
+ path_radiance_accum_light(L, tp, &L_light, shadow, 1.0f, state->bounce, is_lamp);
+ }
+ }
+ }
+#endif
+}
+
+#endif
+
+#endif
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/kernel/kernel_random.h b/intern/cycles/kernel/kernel_random.h
index ac04b3168a1..236f74c0a82 100644
--- a/intern/cycles/kernel/kernel_random.h
+++ b/intern/cycles/kernel/kernel_random.h
@@ -261,12 +261,12 @@ ccl_device uint lcg_init(uint seed)
* For branches in the path we must be careful not to reuse the same number
* in a sequence and offset accordingly. */
-ccl_device_inline float path_state_rng_1D(KernelGlobals *kg, RNG *rng, PathState *state, int dimension)
+ccl_device_inline float path_state_rng_1D(KernelGlobals *kg, RNG *rng, const PathState *state, int dimension)
{
return path_rng_1D(kg, rng, state->sample, state->num_samples, state->rng_offset + dimension);
}
-ccl_device_inline float path_state_rng_1D_for_decision(KernelGlobals *kg, RNG *rng, PathState *state, int dimension)
+ccl_device_inline float path_state_rng_1D_for_decision(KernelGlobals *kg, RNG *rng, const PathState *state, int dimension)
{
/* the rng_offset is not increased for transparent bounces. if we do then
* fully transparent objects can become subtly visible by the different
@@ -279,17 +279,23 @@ ccl_device_inline float path_state_rng_1D_for_decision(KernelGlobals *kg, RNG *r
return path_rng_1D(kg, rng, state->sample, state->num_samples, rng_offset + dimension);
}
-ccl_device_inline void path_state_rng_2D(KernelGlobals *kg, RNG *rng, PathState *state, int dimension, float *fx, float *fy)
+ccl_device_inline void path_state_rng_2D(KernelGlobals *kg, RNG *rng, const PathState *state, int dimension, float *fx, float *fy)
{
path_rng_2D(kg, rng, state->sample, state->num_samples, state->rng_offset + dimension, fx, fy);
}
-ccl_device_inline float path_branched_rng_1D(KernelGlobals *kg, RNG *rng, PathState *state, int branch, int num_branches, int dimension)
+ccl_device_inline float path_branched_rng_1D(KernelGlobals *kg, RNG *rng, const PathState *state, int branch, int num_branches, int dimension)
{
return path_rng_1D(kg, rng, state->sample*num_branches + branch, state->num_samples*num_branches, state->rng_offset + dimension);
}
-ccl_device_inline void path_branched_rng_2D(KernelGlobals *kg, RNG *rng, PathState *state, int branch, int num_branches, int dimension, float *fx, float *fy)
+ccl_device_inline float path_branched_rng_1D_for_decision(KernelGlobals *kg, RNG *rng, const PathState *state, int branch, int num_branches, int dimension)
+{
+ int rng_offset = state->rng_offset + state->transparent_bounce*PRNG_BOUNCE_NUM;
+ return path_rng_1D(kg, rng, state->sample*num_branches + branch, state->num_samples*num_branches, rng_offset + dimension);
+}
+
+ccl_device_inline void path_branched_rng_2D(KernelGlobals *kg, RNG *rng, const PathState *state, int branch, int num_branches, int dimension, float *fx, float *fy)
{
path_rng_2D(kg, rng, state->sample*num_branches + branch, state->num_samples*num_branches, state->rng_offset + dimension, fx, fy);
}
@@ -303,7 +309,7 @@ ccl_device_inline void path_state_branch(PathState *state, int branch, int num_b
state->num_samples = state->num_samples*num_branches;
}
-ccl_device_inline uint lcg_state_init(RNG *rng, PathState *state, uint scramble)
+ccl_device_inline uint lcg_state_init(RNG *rng, const PathState *state, uint scramble)
{
return lcg_init(*rng + state->rng_offset + state->sample*scramble);
}
diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h
index c6a7f9f1570..842b9f68840 100644
--- a/intern/cycles/kernel/kernel_shader.h
+++ b/intern/cycles/kernel/kernel_shader.h
@@ -86,9 +86,8 @@ ccl_device void shader_setup_from_ray(KernelGlobals *kg, ShaderData *sd,
#endif
if(sd->type & PRIMITIVE_TRIANGLE) {
/* static triangle */
- float4 Ns = kernel_tex_fetch(__tri_normal, sd->prim);
- float3 Ng = make_float3(Ns.x, Ns.y, Ns.z);
- sd->shader = __float_as_int(Ns.w);
+ float3 Ng = triangle_normal(kg, sd->prim);
+ sd->shader = __float_as_int(kernel_tex_fetch(__tri_shader, sd->prim));
/* vectors */
sd->P = triangle_refine(kg, sd, isect, ray);
@@ -166,9 +165,8 @@ ccl_device_inline void shader_setup_from_subsurface(KernelGlobals *kg, ShaderDat
/* fetch triangle data */
if(sd->type == PRIMITIVE_TRIANGLE) {
- float4 Ns = kernel_tex_fetch(__tri_normal, sd->prim);
- float3 Ng = make_float3(Ns.x, Ns.y, Ns.z);
- sd->shader = __float_as_int(Ns.w);
+ float3 Ng = triangle_normal(kg, sd->prim);
+ sd->shader = __float_as_int(kernel_tex_fetch(__tri_shader, sd->prim));
/* static triangle */
sd->P = triangle_refine_subsurface(kg, sd, isect, ray);
@@ -860,7 +858,7 @@ ccl_device_inline void _shader_volume_phase_multi_eval(const ShaderData *sd, con
if(phase_pdf != 0.0f) {
bsdf_eval_accum(result_eval, sc->type, eval);
- sum_pdf += phase_pdf;
+ sum_pdf += phase_pdf*sc->sample_weight;
}
sum_sample_weight += sc->sample_weight;
@@ -1028,8 +1026,7 @@ ccl_device bool shader_transparent_shadow(KernelGlobals *kg, Intersection *isect
#ifdef __HAIR__
if(kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE) {
#endif
- float4 Ns = kernel_tex_fetch(__tri_normal, prim);
- shader = __float_as_int(Ns.w);
+ shader = __float_as_int(kernel_tex_fetch(__tri_shader, prim));
#ifdef __HAIR__
}
else {
diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/kernel_textures.h
index b07075c6c95..55960701318 100644
--- a/intern/cycles/kernel/kernel_textures.h
+++ b/intern/cycles/kernel/kernel_textures.h
@@ -36,7 +36,7 @@ KERNEL_TEX(float4, texture_float4, __objects)
KERNEL_TEX(float4, texture_float4, __objects_vector)
/* triangles */
-KERNEL_TEX(float4, texture_float4, __tri_normal)
+KERNEL_TEX(float, texture_float, __tri_shader)
KERNEL_TEX(float4, texture_float4, __tri_vnormal)
KERNEL_TEX(float4, texture_float4, __tri_vindex)
KERNEL_TEX(float4, texture_float4, __tri_verts)
@@ -49,6 +49,7 @@ KERNEL_TEX(float4, texture_float4, __curve_keys)
KERNEL_TEX(uint4, texture_uint4, __attributes_map)
KERNEL_TEX(float, texture_float, __attributes_float)
KERNEL_TEX(float4, texture_float4, __attributes_float3)
+KERNEL_TEX(uchar4, texture_uchar4, __attributes_uchar4)
/* lights */
KERNEL_TEX(float4, texture_float4, __light_distribution)
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index 7edbf5ab172..d81909a623a 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -44,6 +44,8 @@ CCL_NAMESPACE_BEGIN
#define BB_TABLE_YPOWER 5.0f
#define BB_TABLE_SPACING 2.0f
+#define BECKMANN_TABLE_SIZE 256
+
#define TEX_NUM_FLOAT_IMAGES 5
#define SHADER_NONE (~0)
@@ -478,6 +480,7 @@ typedef enum AttributeElement {
ATTR_ELEMENT_VERTEX,
ATTR_ELEMENT_VERTEX_MOTION,
ATTR_ELEMENT_CORNER,
+ ATTR_ELEMENT_CORNER_BYTE,
ATTR_ELEMENT_CURVE,
ATTR_ELEMENT_CURVE_KEY,
ATTR_ELEMENT_CURVE_KEY_MOTION,
@@ -533,10 +536,7 @@ typedef struct ShaderClosure {
float3 T;
#endif
-#ifdef __HAIR__
- float offset;
-#endif
-
+ float data2;
#ifdef __OSL__
void *prim;
#endif
@@ -576,7 +576,8 @@ enum ShaderDataFlag {
SD_AO = 512, /* have ao closure? */
SD_TRANSPARENT = 1024, /* have transparent closure? */
- SD_CLOSURE_FLAGS = (SD_EMISSION|SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_GLOSSY|SD_BSSRDF|SD_HOLDOUT|SD_ABSORPTION|SD_SCATTER|SD_AO),
+ SD_CLOSURE_FLAGS = (SD_EMISSION|SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_GLOSSY|
+ SD_BSSRDF|SD_HOLDOUT|SD_ABSORPTION|SD_SCATTER|SD_AO),
/* shader flags */
SD_USE_MIS = 2048, /* direct light sample */
@@ -585,13 +586,17 @@ enum ShaderDataFlag {
SD_HAS_ONLY_VOLUME = 16384, /* has only volume shader, no surface */
SD_HETEROGENEOUS_VOLUME = 32768, /* has heterogeneous volume */
SD_HAS_BSSRDF_BUMP = 65536, /* bssrdf normal uses bump */
+ SD_VOLUME_EQUIANGULAR = 131072, /* use equiangular sampling */
+ SD_VOLUME_MIS = 262144, /* use multiple importance sampling */
- SD_SHADER_FLAGS = (SD_USE_MIS|SD_HAS_TRANSPARENT_SHADOW|SD_HAS_VOLUME|SD_HAS_ONLY_VOLUME|SD_HETEROGENEOUS_VOLUME|SD_HAS_BSSRDF_BUMP),
+ SD_SHADER_FLAGS = (SD_USE_MIS|SD_HAS_TRANSPARENT_SHADOW|SD_HAS_VOLUME|
+ SD_HAS_ONLY_VOLUME|SD_HETEROGENEOUS_VOLUME|
+ SD_HAS_BSSRDF_BUMP|SD_VOLUME_EQUIANGULAR|SD_VOLUME_MIS),
/* object flags */
- SD_HOLDOUT_MASK = 131072, /* holdout for camera rays */
- SD_OBJECT_MOTION = 262144, /* has object motion blur */
- SD_TRANSFORM_APPLIED = 524288, /* vertices have transform applied */
+ SD_HOLDOUT_MASK = 524288, /* holdout for camera rays */
+ SD_OBJECT_MOTION = 1048576, /* has object motion blur */
+ SD_TRANSFORM_APPLIED = 2097152, /* vertices have transform applied */
SD_OBJECT_FLAGS = (SD_HOLDOUT_MASK|SD_OBJECT_MOTION|SD_TRANSFORM_APPLIED)
};
@@ -893,7 +898,6 @@ typedef struct KernelIntegrator {
int aa_samples;
/* volume render */
- int volume_homogeneous_sampling;
int use_volumes;
int volume_max_steps;
float volume_step_size;
@@ -931,11 +935,11 @@ typedef struct KernelCurves {
float maximum_width;
} KernelCurves;
-typedef struct KernelBlackbody {
- int table_offset;
- int pad1, pad2, pad3;
-} KernelBlackbody;
-
+typedef struct KernelTables {
+ int blackbody_offset;
+ int beckmann_offset;
+ int pad1, pad2;
+} KernelTables;
typedef struct KernelData {
KernelCamera cam;
@@ -944,7 +948,7 @@ typedef struct KernelData {
KernelIntegrator integrator;
KernelBVH bvh;
KernelCurves curve;
- KernelBlackbody blackbody;
+ KernelTables tables;
} KernelData;
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h
index 49e4cf64de4..e61a29fb627 100644
--- a/intern/cycles/kernel/kernel_volume.h
+++ b/intern/cycles/kernel/kernel_volume.h
@@ -116,6 +116,36 @@ ccl_device bool volume_stack_is_heterogeneous(KernelGlobals *kg, VolumeStack *st
return false;
}
+ccl_device int volume_stack_sampling_method(KernelGlobals *kg, VolumeStack *stack)
+{
+ if(kernel_data.integrator.num_all_lights == 0)
+ return 0;
+
+ int method = -1;
+
+ for(int i = 0; stack[i].shader != SHADER_NONE; i++) {
+ int shader_flag = kernel_tex_fetch(__shader_flag, (stack[i].shader & SHADER_MASK)*2);
+
+ if(shader_flag & SD_VOLUME_MIS) {
+ return SD_VOLUME_MIS;
+ }
+ else if(shader_flag & SD_VOLUME_EQUIANGULAR) {
+ if(method == 0)
+ return SD_VOLUME_MIS;
+
+ method = SD_VOLUME_EQUIANGULAR;
+ }
+ else {
+ if(method == SD_VOLUME_EQUIANGULAR)
+ return SD_VOLUME_MIS;
+
+ method = 0;
+ }
+ }
+
+ return method;
+}
+
/* Volume Shadows
*
* These functions are used to attenuate shadow rays to lights. Both absorption
@@ -136,7 +166,7 @@ ccl_device void kernel_volume_shadow_homogeneous(KernelGlobals *kg, PathState *s
ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg, PathState *state, Ray *ray, ShaderData *sd, float3 *throughput)
{
float3 tp = *throughput;
- const float tp_eps = 1e-10f; /* todo: this is likely not the right value */
+ const float tp_eps = 1e-6f; /* todo: this is likely not the right value */
/* prepare for stepping */
int max_steps = kernel_data.integrator.volume_max_steps;
@@ -226,25 +256,6 @@ ccl_device float kernel_volume_equiangular_pdf(Ray *ray, float3 light_P, float s
return pdf;
}
-ccl_device bool kernel_volume_equiangular_light_position(KernelGlobals *kg, PathState *state, Ray *ray, RNG *rng, float3 *light_P, bool *distant)
-{
- /* light RNGs */
- float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT);
- float light_u, light_v;
- path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
-
- /* light sample */
- LightSample ls;
- light_sample(kg, light_t, light_u, light_v, ray->time, ray->P, &ls);
- if(ls.pdf == 0.0f)
- return false;
-
- *light_P = ls.P;
- *distant = ls.t == FLT_MAX;
-
- return true;
-}
-
/* Distance sampling */
ccl_device float kernel_volume_distance_sample(float max_t, float3 sigma_t, int channel, float xi, float3 *transmittance, float3 *pdf)
@@ -304,7 +315,7 @@ ccl_device float3 kernel_volume_emission_integrate(VolumeShaderCoefficients *coe
* the volume shading coefficient for the entire line segment */
ccl_device VolumeIntegrateResult kernel_volume_integrate_homogeneous(KernelGlobals *kg,
PathState *state, Ray *ray, ShaderData *sd, PathRadiance *L, float3 *throughput,
- RNG *rng)
+ RNG *rng, bool probalistic_scatter)
{
VolumeShaderCoefficients coeff;
@@ -326,47 +337,37 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_homogeneous(KernelGloba
int channel = (int)(rphase*3.0f);
sd->randb_closure = rphase*3.0f - channel;
+ /* decide if we will hit or miss */
+ bool scatter = true;
float xi = path_state_rng_1D_for_decision(kg, rng, state, PRNG_SCATTER_DISTANCE);
- /* decide if we will hit or miss */
- float sample_sigma_t = kernel_volume_channel_get(sigma_t, channel);
- float sample_transmittance = expf(-sample_sigma_t * t);
+ if(probalistic_scatter) {
+ float sample_sigma_t = kernel_volume_channel_get(sigma_t, channel);
+ float sample_transmittance = expf(-sample_sigma_t * t);
+
+ if(1.0f - xi >= sample_transmittance) {
+ scatter = true;
+
+ /* rescale random number so we can reuse it */
+ xi = 1.0f - (1.0f - xi - sample_transmittance)/(1.0f - sample_transmittance);
- if(xi >= sample_transmittance) {
+ }
+ else
+ scatter = false;
+ }
+
+ if(scatter) {
/* scattering */
float3 pdf;
float3 transmittance;
float sample_t;
- /* rescale random number so we can reuse it */
- xi = (xi - sample_transmittance)/(1.0f - sample_transmittance);
-
- if(kernel_data.integrator.volume_homogeneous_sampling == 0 || !kernel_data.integrator.num_all_lights) {
- /* distance sampling */
- sample_t = kernel_volume_distance_sample(ray->t, sigma_t, channel, xi, &transmittance, &pdf);
- }
- else {
- /* equiangular sampling */
- float3 light_P;
- float equi_pdf;
- bool light_distant;
-
- if(!kernel_volume_equiangular_light_position(kg, state, ray, rng, &light_P, &light_distant))
- return VOLUME_PATH_MISSED;
-
- if(light_distant) {
- /* distant light, revert to distance sampling because position is infinitely far away */
- sample_t = kernel_volume_distance_sample(ray->t, sigma_t, channel, xi, &transmittance, &pdf);
- }
- else {
- sample_t = kernel_volume_equiangular_sample(ray, light_P, xi, &equi_pdf);
- transmittance = volume_color_transmittance(sigma_t, sample_t);
- pdf = make_float3(equi_pdf, equi_pdf, equi_pdf);
- }
- }
+ /* distance sampling */
+ sample_t = kernel_volume_distance_sample(ray->t, sigma_t, channel, xi, &transmittance, &pdf);
/* modifiy pdf for hit/miss decision */
- pdf *= make_float3(1.0f, 1.0f, 1.0f) - volume_color_transmittance(sigma_t, t);
+ if(probalistic_scatter)
+ pdf *= make_float3(1.0f, 1.0f, 1.0f) - volume_color_transmittance(sigma_t, t);
new_tp = *throughput * coeff.sigma_s * transmittance / average(pdf);
t = sample_t;
@@ -385,7 +386,7 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_homogeneous(KernelGloba
}
/* integrate emission attenuated by extinction */
- if(closure_flag & SD_EMISSION) {
+ if(L && (closure_flag & SD_EMISSION)) {
float3 sigma_t = coeff.sigma_a + coeff.sigma_s;
float3 transmittance = volume_color_transmittance(sigma_t, ray->t);
float3 emission = kernel_volume_emission_integrate(&coeff, closure_flag, transmittance, ray->t);
@@ -408,13 +409,15 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_homogeneous(KernelGloba
return VOLUME_PATH_ATTENUATED;
}
-/* heterogeneous volume: integrate stepping through the volume until we
- * reach the end, get absorbed entirely, or run out of iterations */
-ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous(KernelGlobals *kg,
+/* heterogeneous volume distance sampling: integrate stepping through the
+ * volume until we reach the end, get absorbed entirely, or run out of
+ * iterations. this does probalistically scatter or get transmitted through
+ * for path tracing where we don't want to branch. */
+ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance(KernelGlobals *kg,
PathState *state, Ray *ray, ShaderData *sd, PathRadiance *L, float3 *throughput, RNG *rng)
{
float3 tp = *throughput;
- const float tp_eps = 1e-10f; /* todo: this is likely not the right value */
+ const float tp_eps = 1e-6f; /* todo: this is likely not the right value */
/* prepare for stepping */
int max_steps = kernel_data.integrator.volume_max_steps;
@@ -425,9 +428,12 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous(KernelGlo
float t = 0.0f;
float3 accum_transmittance = make_float3(1.0f, 1.0f, 1.0f);
- /* cache some constant variables */
- float xi;
- int channel = -1;
+ /* pick random color channel, we use the Veach one-sample
+ * model with balance heuristic for the channels */
+ float xi = path_state_rng_1D_for_decision(kg, rng, state, PRNG_SCATTER_DISTANCE);
+ float rphase = path_state_rng_1D_for_decision(kg, rng, state, PRNG_PHASE);
+ int channel = (int)(rphase*3.0f);
+ sd->randb_closure = rphase*3.0f - channel;
bool has_scatter = false;
for(int i = 0; i < max_steps; i++) {
@@ -449,25 +455,13 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous(KernelGlo
float3 transmittance;
bool scatter = false;
- /* randomly scatter, and if we do dt and new_t are shortened */
+ /* distance sampling */
if((closure_flag & SD_SCATTER) || (has_scatter && (closure_flag & SD_ABSORPTION))) {
has_scatter = true;
- /* average sigma_t and sigma_s over segment */
float3 sigma_t = coeff.sigma_a + coeff.sigma_s;
float3 sigma_s = coeff.sigma_s;
- /* lazily set up variables for sampling */
- if(channel == -1) {
- /* pick random color channel, we use the Veach one-sample
- * model with balance heuristic for the channels */
- xi = path_state_rng_1D_for_decision(kg, rng, state, PRNG_SCATTER_DISTANCE);
-
- float rphase = path_state_rng_1D_for_decision(kg, rng, state, PRNG_PHASE);
- channel = (int)(rphase*3.0f);
- sd->randb_closure = rphase*3.0f - channel;
- }
-
/* compute transmittance over full step */
transmittance = volume_color_transmittance(sigma_t, dt);
@@ -480,10 +474,12 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous(KernelGlo
float new_dt = -logf(1.0f - xi)/sample_sigma_t;
new_t = t + new_dt;
- /* transmittance, throughput */
+ /* transmittance and pdf */
float3 new_transmittance = volume_color_transmittance(sigma_t, new_dt);
- float pdf = average(sigma_t * new_transmittance);
- new_tp = tp * sigma_s * new_transmittance / pdf;
+ float3 pdf = sigma_t * new_transmittance;
+
+ /* throughput */
+ new_tp = tp * sigma_s * new_transmittance / average(pdf);
scatter = true;
}
else {
@@ -504,7 +500,7 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous(KernelGlo
}
/* integrate emission attenuated by absorption */
- if(closure_flag & SD_EMISSION) {
+ if(L && (closure_flag & SD_EMISSION)) {
float3 emission = kernel_volume_emission_integrate(&coeff, closure_flag, transmittance, dt);
path_radiance_accum_emission(L, tp, emission, state->bounce);
}
@@ -518,19 +514,19 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous(KernelGlo
tp = make_float3(0.0f, 0.0f, 0.0f);
break;
}
+ }
- /* prepare to scatter to new direction */
- if(scatter) {
- /* adjust throughput and move to new location */
- sd->P = ray->P + new_t*ray->D;
- *throughput = tp;
+ /* prepare to scatter to new direction */
+ if(scatter) {
+ /* adjust throughput and move to new location */
+ sd->P = ray->P + new_t*ray->D;
+ *throughput = tp;
- return VOLUME_PATH_SCATTERED;
- }
- else {
- /* accumulate transmittance */
- accum_transmittance *= transmittance;
- }
+ return VOLUME_PATH_SCATTERED;
+ }
+ else {
+ /* accumulate transmittance */
+ accum_transmittance *= transmittance;
}
}
@@ -545,14 +541,35 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous(KernelGlo
return VOLUME_PATH_ATTENUATED;
}
+/* get the volume attenuation and emission over line segment defined by
+ * ray, with the assumption that there are no surfaces blocking light
+ * between the endpoints. distance sampling is used to decide if we will
+ * scatter or not. */
+ccl_device_noinline VolumeIntegrateResult kernel_volume_integrate(KernelGlobals *kg,
+ PathState *state, ShaderData *sd, Ray *ray, PathRadiance *L, float3 *throughput, RNG *rng)
+{
+ /* workaround to fix correlation bug in T38710, can find better solution
+ * in random number generator later, for now this is done here to not impact
+ * performance of rendering without volumes */
+ RNG tmp_rng = cmj_hash(*rng, state->rng_offset);
+ bool heterogeneous = volume_stack_is_heterogeneous(kg, state->volume_stack);
+
+ shader_setup_from_volume(kg, sd, ray, state->bounce, state->transparent_bounce);
+
+ if(heterogeneous)
+ return kernel_volume_integrate_heterogeneous_distance(kg, state, ray, sd, L, throughput, &tmp_rng);
+ else
+ return kernel_volume_integrate_homogeneous(kg, state, ray, sd, L, throughput, &tmp_rng, true);
+}
+
/* Decoupled Volume Sampling
*
* VolumeSegment is list of coefficients and transmittance stored at all steps
* through a volume. This can then latter be used for decoupled sampling as in:
- * "Importance Sampling Techniques for Path Tracing in Participating Media" */
-
-/* CPU only because of malloc/free */
-#ifdef __KERNEL_CPU__
+ * "Importance Sampling Techniques for Path Tracing in Participating Media"
+ *
+ * On the GPU this is only supported for homogeneous volumes (1 step), due to
+ * no support for malloc/free and too much stack usage with a fix size array. */
typedef struct VolumeStep {
float3 sigma_s; /* scatter coefficient */
@@ -565,12 +582,18 @@ typedef struct VolumeStep {
} VolumeStep;
typedef struct VolumeSegment {
+#ifdef __KERNEL_CPU__
VolumeStep *steps; /* recorded steps */
+#else
+ VolumeStep steps[1]; /* recorded steps */
+#endif
int numsteps; /* number of steps */
int closure_flag; /* accumulated closure flags from all steps */
float3 accum_emission; /* accumulated emission at end of segment */
float3 accum_transmittance; /* accumulated transmittance at end of segment */
+
+ int sampling_method; /* volume sampling method */
} VolumeSegment;
/* record volume steps to the end of the volume.
@@ -582,6 +605,8 @@ typedef struct VolumeSegment {
ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *state,
Ray *ray, ShaderData *sd, VolumeSegment *segment, bool heterogeneous)
{
+ const float tp_eps = 1e-6f; /* todo: this is likely not the right value */
+
/* prepare for volume stepping */
int max_steps;
float step_size, random_jitter_offset;
@@ -608,7 +633,11 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
segment->closure_flag = 0;
segment->numsteps = 0;
+#ifdef __KERNEL_CPU__
segment->steps = (VolumeStep*)malloc(sizeof(VolumeStep)*max_steps);
+#else
+ kernel_assert(max_steps == 1);
+#endif
VolumeStep *step = segment->steps;
@@ -669,6 +698,10 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
t = new_t;
if(t == ray->t)
break;
+
+ /* stop if nearly all light blocked */
+ if(accum_transmittance.x < tp_eps && accum_transmittance.y < tp_eps && accum_transmittance.z < tp_eps)
+ break;
}
/* store total emission and transmittance */
@@ -690,7 +723,9 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
ccl_device void kernel_volume_decoupled_free(KernelGlobals *kg, VolumeSegment *segment)
{
+#ifdef __KERNEL_CPU__
free(segment->steps);
+#endif
}
/* scattering for homogeneous and heterogeneous volumes, using decoupled ray
@@ -701,7 +736,8 @@ ccl_device void kernel_volume_decoupled_free(KernelGlobals *kg, VolumeSegment *s
* these also do not do emission or modify throughput. */
ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter(
KernelGlobals *kg, PathState *state, Ray *ray, ShaderData *sd,
- float3 *throughput, RNG *rng, VolumeSegment *segment)
+ float3 *throughput, float rphase, float rscatter,
+ const VolumeSegment *segment, const float3 *light_P, bool probalistic_scatter)
{
int closure_flag = segment->closure_flag;
@@ -710,38 +746,58 @@ ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter(
/* pick random color channel, we use the Veach one-sample
* model with balance heuristic for the channels */
- float rphase = path_state_rng_1D_for_decision(kg, rng, state, PRNG_PHASE);
int channel = (int)(rphase*3.0f);
sd->randb_closure = rphase*3.0f - channel;
+ float xi = rscatter;
- float xi = path_state_rng_1D_for_decision(kg, rng, state, PRNG_SCATTER_DISTANCE);
+ /* probalistic scattering decision based on transmittance */
+ if(probalistic_scatter) {
+ float sample_transmittance = kernel_volume_channel_get(segment->accum_transmittance, channel);
+
+ if(1.0f - xi >= sample_transmittance) {
+ /* rescale random number so we can reuse it */
+ xi = 1.0f - (1.0f - xi - sample_transmittance)/(1.0f - sample_transmittance);
+ }
+ else {
+ *throughput /= sample_transmittance;
+ return VOLUME_PATH_MISSED;
+ }
+ }
VolumeStep *step;
float3 transmittance;
float pdf, sample_t;
+ float mis_weight = 1.0f;
+ bool distance_sample = true;
+ bool use_mis = false;
+
+ if(segment->sampling_method && light_P) {
+ if(segment->sampling_method == SD_VOLUME_MIS) {
+ /* multiple importance sample: randomly pick between
+ * equiangular and distance sampling strategy */
+ if(xi < 0.5f) {
+ xi *= 2.0f;
+ }
+ else {
+ xi = (xi - 0.5f)*2.0f;
+ distance_sample = false;
+ }
- /* pick position on light for equiangular */
- bool equiangular = (kernel_data.integrator.volume_homogeneous_sampling != 0 && kernel_data.integrator.num_all_lights);
- float3 light_P;
-
- if(equiangular) {
- bool light_distant;
-
- if(!kernel_volume_equiangular_light_position(kg, state, ray, rng, &light_P, &light_distant))
- return VOLUME_PATH_MISSED;
-
- /* distant light, revert to distance sampling because position is infinitely far away */
- if(light_distant)
- equiangular = false;
+ use_mis = true;
+ }
+ else {
+ /* only equiangular sampling */
+ distance_sample = false;
+ }
}
/* distance sampling */
- if(!equiangular) {
+ if(distance_sample) {
/* find step in cdf */
step = segment->steps;
float prev_t = 0.0f;
- float3 step_pdf = make_float3(1.0f, 1.0f, 1.0f);
+ float3 step_pdf_distance = make_float3(1.0f, 1.0f, 1.0f);
if(segment->numsteps > 1) {
float prev_cdf = 0.0f;
@@ -764,7 +820,7 @@ ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter(
xi = (xi - prev_cdf)/(step_cdf - prev_cdf);
/* pdf for picking step */
- step_pdf = step->cdf_distance - prev_cdf_distance;
+ step_pdf_distance = step->cdf_distance - prev_cdf_distance;
}
/* determine range in which we will sample */
@@ -773,30 +829,59 @@ ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter(
/* sample distance and compute transmittance */
float3 distance_pdf;
sample_t = prev_t + kernel_volume_distance_sample(step_t, step->sigma_t, channel, xi, &transmittance, &distance_pdf);
- pdf = average(distance_pdf * step_pdf);
+
+ /* modifiy pdf for hit/miss decision */
+ if(probalistic_scatter)
+ distance_pdf *= make_float3(1.0f, 1.0f, 1.0f) - segment->accum_transmittance;
+
+ pdf = average(distance_pdf * step_pdf_distance);
+
+ /* multiple importance sampling */
+ if(use_mis) {
+ float equi_pdf = kernel_volume_equiangular_pdf(ray, *light_P, sample_t);
+ mis_weight = 2.0f*power_heuristic(pdf, equi_pdf);
+ }
}
/* equi-angular sampling */
else {
/* sample distance */
- sample_t = kernel_volume_equiangular_sample(ray, light_P, xi, &pdf);
+ sample_t = kernel_volume_equiangular_sample(ray, *light_P, xi, &pdf);
/* find step in which sampled distance is located */
step = segment->steps;
float prev_t = 0.0f;
+ float3 step_pdf_distance = make_float3(1.0f, 1.0f, 1.0f);
if(segment->numsteps > 1) {
/* todo: optimize using binary search */
+ float3 prev_cdf_distance = make_float3(0.0f, 0.0f, 0.0f);
+
for(int i = 0; i < segment->numsteps-1; i++, step++) {
if(sample_t < step->t)
break;
prev_t = step->t;
+ prev_cdf_distance = step->cdf_distance;
}
+
+ /* pdf for picking step with distance sampling */
+ step_pdf_distance = step->cdf_distance - prev_cdf_distance;
}
-
+
+ /* determine range in which we will sample */
+ float step_t = step->t - prev_t;
+ float step_sample_t = sample_t - prev_t;
+
/* compute transmittance */
- transmittance = volume_color_transmittance(step->sigma_t, sample_t - prev_t);
+ transmittance = volume_color_transmittance(step->sigma_t, step_sample_t);
+
+ /* multiple importance sampling */
+ if(use_mis) {
+ float3 distance_pdf3 = kernel_volume_distance_pdf(step_t, step->sigma_t, step_sample_t);
+ float distance_pdf = average(distance_pdf3 * step_pdf_distance);
+ mis_weight = 2.0f*power_heuristic(pdf, distance_pdf);
+ }
}
/* compute transmittance up to this step */
@@ -804,7 +889,7 @@ ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter(
transmittance *= (step-1)->accum_transmittance;
/* modify throughput */
- *throughput *= step->sigma_s * transmittance / pdf;
+ *throughput *= step->sigma_s * transmittance * (mis_weight / pdf);
/* evaluate shader to create closures at shading point */
if(segment->numsteps > 1) {
@@ -820,40 +905,27 @@ ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter(
return VOLUME_PATH_SCATTERED;
}
-#endif
-
-/* get the volume attenuation and emission over line segment defined by
- * ray, with the assumption that there are no surfaces blocking light
- * between the endpoints */
-ccl_device_noinline VolumeIntegrateResult kernel_volume_integrate(KernelGlobals *kg,
- PathState *state, ShaderData *sd, Ray *ray, PathRadiance *L, float3 *throughput, RNG *rng)
+/* decide if we need to use decoupled or not */
+ccl_device bool kernel_volume_use_decoupled(KernelGlobals *kg, bool heterogeneous, bool direct, int sampling_method)
{
- /* workaround to fix correlation bug in T38710, can find better solution
- * in random number generator later, for now this is done here to not impact
- * performance of rendering without volumes */
- RNG tmp_rng = cmj_hash(*rng, state->rng_offset);
- bool heterogeneous = volume_stack_is_heterogeneous(kg, state->volume_stack);
-
-#if 0
- /* debugging code to compare decoupled ray marching */
- VolumeSegment segment;
-
- shader_setup_from_volume(kg, sd, ray, state->bounce, state->transparent_bounce);
- kernel_volume_decoupled_record(kg, state, ray, sd, &segment, heterogeneous);
-
- VolumeIntegrateResult result = kernel_volume_decoupled_scatter(kg, state, ray, sd, throughput, &tmp_rng, &segment);
+ /* decoupled ray marching for heterogenous volumes not supported on the GPU,
+ * which also means equiangular and multiple importance sampling is not
+ * support for that case */
+#ifdef __KERNEL_GPU__
+ if(heterogeneous)
+ return false;
+#endif
- kernel_volume_decoupled_free(kg, &segment);
+ /* equiangular and multiple importance sampling only implemented for decoupled */
+ if(sampling_method != 0)
+ return true;
- return result;
-#else
- shader_setup_from_volume(kg, sd, ray, state->bounce, state->transparent_bounce);
-
- if(heterogeneous)
- return kernel_volume_integrate_heterogeneous(kg, state, ray, sd, L, throughput, &tmp_rng);
+ /* for all light sampling use decoupled, reusing shader evaluations is
+ * typically faster in that case */
+ if(direct)
+ return kernel_data.integrator.sample_all_lights_direct;
else
- return kernel_volume_integrate_homogeneous(kg, state, ray, sd, L, throughput, &tmp_rng);
-#endif
+ return kernel_data.integrator.sample_all_lights_indirect;
}
/* Volume Stack
diff --git a/intern/cycles/kernel/osl/osl_closures.cpp b/intern/cycles/kernel/osl/osl_closures.cpp
index a96c0e2b1fb..f102e79f5c7 100644
--- a/intern/cycles/kernel/osl/osl_closures.cpp
+++ b/intern/cycles/kernel/osl/osl_closures.cpp
@@ -41,6 +41,8 @@
#include "util_param.h"
#include "kernel_types.h"
+#include "kernel_compat_cpu.h"
+#include "kernel_globals.h"
#include "kernel_montecarlo.h"
#include "closure/bsdf_util.h"
@@ -51,7 +53,7 @@
#include "closure/bsdf_reflection.h"
#include "closure/bsdf_refraction.h"
#include "closure/bsdf_transparent.h"
-#include "closure/bsdf_ward.h"
+#include "closure/bsdf_ashikhmin_shirley.h"
#include "closure/bsdf_westin.h"
#include "closure/bsdf_toon.h"
#include "closure/bsdf_hair.h"
@@ -103,12 +105,12 @@ BSDF_CLOSURE_CLASS_BEGIN(AshikhminVelvet, ashikhmin_velvet, ashikhmin_velvet, LA
CLOSURE_FLOAT_PARAM(AshikhminVelvetClosure, sc.data0),
BSDF_CLOSURE_CLASS_END(AshikhminVelvet, ashikhmin_velvet)
-BSDF_CLOSURE_CLASS_BEGIN(Ward, ward, ward, LABEL_GLOSSY)
- CLOSURE_FLOAT3_PARAM(WardClosure, sc.N),
- CLOSURE_FLOAT3_PARAM(WardClosure, sc.T),
- CLOSURE_FLOAT_PARAM(WardClosure, sc.data0),
- CLOSURE_FLOAT_PARAM(WardClosure, sc.data1),
-BSDF_CLOSURE_CLASS_END(Ward, ward)
+BSDF_CLOSURE_CLASS_BEGIN(AshikhminShirley, ashikhmin_shirley_aniso, ashikhmin_shirley, LABEL_GLOSSY)
+ CLOSURE_FLOAT3_PARAM(AshikhminShirleyClosure, sc.N),
+ CLOSURE_FLOAT3_PARAM(AshikhminShirleyClosure, sc.T),
+ CLOSURE_FLOAT_PARAM(AshikhminShirleyClosure, sc.data0),
+ CLOSURE_FLOAT_PARAM(AshikhminShirleyClosure, sc.data1),
+BSDF_CLOSURE_CLASS_END(AshikhminShirley, ashikhmin_shirley_aniso)
BSDF_CLOSURE_CLASS_BEGIN(DiffuseToon, diffuse_toon, diffuse_toon, LABEL_DIFFUSE)
CLOSURE_FLOAT3_PARAM(DiffuseToonClosure, sc.N),
@@ -127,11 +129,25 @@ BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGX, microfacet_ggx, microfacet_ggx, LABEL_GL
CLOSURE_FLOAT_PARAM(MicrofacetGGXClosure, sc.data0),
BSDF_CLOSURE_CLASS_END(MicrofacetGGX, microfacet_ggx)
+BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGXAniso, microfacet_ggx_aniso, microfacet_ggx, LABEL_GLOSSY)
+ CLOSURE_FLOAT3_PARAM(MicrofacetGGXAnisoClosure, sc.N),
+ CLOSURE_FLOAT3_PARAM(MicrofacetGGXAnisoClosure, sc.T),
+ CLOSURE_FLOAT_PARAM(MicrofacetGGXAnisoClosure, sc.data0),
+ CLOSURE_FLOAT_PARAM(MicrofacetGGXAnisoClosure, sc.data1),
+BSDF_CLOSURE_CLASS_END(MicrofacetGGXAniso, microfacet_ggx_aniso)
+
BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmann, microfacet_beckmann, microfacet_beckmann, LABEL_GLOSSY)
CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannClosure, sc.N),
CLOSURE_FLOAT_PARAM(MicrofacetBeckmannClosure, sc.data0),
BSDF_CLOSURE_CLASS_END(MicrofacetBeckmann, microfacet_beckmann)
+BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmannAniso, microfacet_beckmann_aniso, microfacet_beckmann, LABEL_GLOSSY)
+ CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannAnisoClosure, sc.N),
+ CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannAnisoClosure, sc.T),
+ CLOSURE_FLOAT_PARAM(MicrofacetBeckmannAnisoClosure, sc.data0),
+ CLOSURE_FLOAT_PARAM(MicrofacetBeckmannAnisoClosure, sc.data1),
+BSDF_CLOSURE_CLASS_END(MicrofacetBeckmannAniso, microfacet_beckmann_aniso)
+
BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGXRefraction, microfacet_ggx_refraction, microfacet_ggx, LABEL_GLOSSY)
CLOSURE_FLOAT3_PARAM(MicrofacetGGXRefractionClosure, sc.N),
CLOSURE_FLOAT_PARAM(MicrofacetGGXRefractionClosure, sc.data0),
@@ -150,7 +166,7 @@ BSDF_CLOSURE_CLASS_BEGIN(HairReflection, hair_reflection, hair_reflection, LABEL
CLOSURE_FLOAT_PARAM(HairReflectionClosure, sc.data1),
#ifdef __HAIR__
CLOSURE_FLOAT3_PARAM(HairReflectionClosure, sc.T),
- CLOSURE_FLOAT_PARAM(HairReflectionClosure, sc.offset),
+ CLOSURE_FLOAT_PARAM(HairReflectionClosure, sc.data2),
#else
CLOSURE_FLOAT3_PARAM(HairReflectionClosure, sc.N),
CLOSURE_FLOAT_PARAM(HairReflectionClosure, sc.data1),
@@ -163,7 +179,7 @@ BSDF_CLOSURE_CLASS_BEGIN(HairTransmission, hair_transmission, hair_transmission,
CLOSURE_FLOAT_PARAM(HairTransmissionClosure, sc.data1),
#ifdef __HAIR__
CLOSURE_FLOAT3_PARAM(HairReflectionClosure, sc.T),
- CLOSURE_FLOAT_PARAM(HairReflectionClosure, sc.offset),
+ CLOSURE_FLOAT_PARAM(HairReflectionClosure, sc.data2),
#else
CLOSURE_FLOAT3_PARAM(HairReflectionClosure, sc.N),
CLOSURE_FLOAT_PARAM(HairReflectionClosure, sc.data1),
@@ -210,14 +226,18 @@ void OSLShader::register_closures(OSLShadingSystem *ss_)
bsdf_transparent_params(), bsdf_transparent_prepare);
register_closure(ss, "microfacet_ggx", id++,
bsdf_microfacet_ggx_params(), bsdf_microfacet_ggx_prepare);
+ register_closure(ss, "microfacet_ggx_aniso", id++,
+ bsdf_microfacet_ggx_aniso_params(), bsdf_microfacet_ggx_aniso_prepare);
register_closure(ss, "microfacet_ggx_refraction", id++,
bsdf_microfacet_ggx_refraction_params(), bsdf_microfacet_ggx_refraction_prepare);
register_closure(ss, "microfacet_beckmann", id++,
bsdf_microfacet_beckmann_params(), bsdf_microfacet_beckmann_prepare);
+ register_closure(ss, "microfacet_beckmann_aniso", id++,
+ bsdf_microfacet_beckmann_aniso_params(), bsdf_microfacet_beckmann_aniso_prepare);
register_closure(ss, "microfacet_beckmann_refraction", id++,
bsdf_microfacet_beckmann_refraction_params(), bsdf_microfacet_beckmann_refraction_prepare);
- register_closure(ss, "ward", id++,
- bsdf_ward_params(), bsdf_ward_prepare);
+ register_closure(ss, "ashikhmin_shirley", id++,
+ bsdf_ashikhmin_shirley_aniso_params(), bsdf_ashikhmin_shirley_aniso_prepare);
register_closure(ss, "ashikhmin_velvet", id++,
bsdf_ashikhmin_velvet_params(), bsdf_ashikhmin_velvet_prepare);
register_closure(ss, "diffuse_toon", id++,
diff --git a/intern/cycles/kernel/osl/osl_closures.h b/intern/cycles/kernel/osl/osl_closures.h
index 218cf1c19cc..a543907e884 100644
--- a/intern/cycles/kernel/osl/osl_closures.h
+++ b/intern/cycles/kernel/osl/osl_closures.h
@@ -149,17 +149,18 @@ public: \
\
void blur(float roughness) \
{ \
- bsdf_##svmlower##_blur(&sc, roughness); \
} \
\
float3 eval_reflect(const float3 &omega_out, const float3 &omega_in, float& pdf) const \
{ \
- return bsdf_##svmlower##_eval_reflect(&sc, omega_out, omega_in, &pdf); \
+ pdf = 0; \
+ return make_float3(0, 0, 0); \
} \
\
float3 eval_transmit(const float3 &omega_out, const float3 &omega_in, float& pdf) const \
{ \
- return bsdf_##svmlower##_eval_transmit(&sc, omega_out, omega_in, &pdf); \
+ pdf = 0; \
+ return make_float3(0, 0, 0); \
} \
\
int sample(const float3 &Ng, \
@@ -168,8 +169,8 @@ public: \
float3 &omega_in, float3 &domega_in_dx, float3 &domega_in_dy, \
float &pdf, float3 &eval) const \
{ \
- return bsdf_##svmlower##_sample(&sc, Ng, omega_out, domega_out_dx, domega_out_dy, \
- randu, randv, &eval, &omega_in, &domega_in_dx, &domega_in_dy, &pdf); \
+ pdf = 0; \
+ return LABEL_NONE; \
} \
}; \
\
diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp
index 8fe48b9b38c..6a59a381f48 100644
--- a/intern/cycles/kernel/osl/osl_services.cpp
+++ b/intern/cycles/kernel/osl/osl_services.cpp
@@ -871,14 +871,30 @@ bool OSLRenderServices::texture(ustring filename, TextureOpt &options,
return true;
}
#endif
+ bool status;
- OSLThreadData *tdata = kg->osl_tdata;
- OIIO::TextureSystem::Perthread *thread_info = tdata->oiio_thread_info;
+ if(filename[0] == '@' && filename.find('.') == -1) {
+ int slot = atoi(filename.c_str() + 1);
+ float4 rgba = kernel_tex_image_interp(slot, s, 1.0f - t);
- OIIO::TextureSystem::TextureHandle *th = ts->get_texture_handle(filename, thread_info);
+ result[0] = rgba[0];
+ if(options.nchannels > 1)
+ result[1] = rgba[1];
+ if(options.nchannels > 2)
+ result[2] = rgba[2];
+ if(options.nchannels > 3)
+ result[3] = rgba[3];
+ status = true;
+ }
+ else {
+ OSLThreadData *tdata = kg->osl_tdata;
+ OIIO::TextureSystem::Perthread *thread_info = tdata->oiio_thread_info;
+
+ OIIO::TextureSystem::TextureHandle *th = ts->get_texture_handle(filename, thread_info);
- bool status = ts->texture(th, thread_info,
- options, s, t, dsdx, dtdx, dsdy, dtdy, result);
+ status = ts->texture(th, thread_info,
+ options, s, t, dsdx, dtdx, dsdy, dtdy, result);
+ }
if(!status) {
if(options.nchannels == 3 || options.nchannels == 4) {
diff --git a/intern/cycles/kernel/osl/osl_shader.cpp b/intern/cycles/kernel/osl/osl_shader.cpp
index 843dcdd0985..28135784db9 100644
--- a/intern/cycles/kernel/osl/osl_shader.cpp
+++ b/intern/cycles/kernel/osl/osl_shader.cpp
@@ -181,12 +181,9 @@ static void flatten_surface_closure_tree(ShaderData *sd, int path_flag,
sc.T = bsdf->sc.T;
sc.data0 = bsdf->sc.data0;
sc.data1 = bsdf->sc.data1;
+ sc.data2 = bsdf->sc.data2;
sc.prim = bsdf->sc.prim;
-#ifdef __HAIR__
- sc.offset = bsdf->sc.offset;
-#endif
-
/* add */
if(sc.sample_weight > CLOSURE_WEIGHT_CUTOFF && sd->num_closure < MAX_CLOSURE) {
sd->closure[sd->num_closure++] = sc;
@@ -202,6 +199,7 @@ static void flatten_surface_closure_tree(ShaderData *sd, int path_flag,
sc.type = CLOSURE_EMISSION_ID;
sc.data0 = 0.0f;
sc.data1 = 0.0f;
+ sc.data2 = 0.0f;
sc.prim = NULL;
/* flag */
@@ -219,6 +217,7 @@ static void flatten_surface_closure_tree(ShaderData *sd, int path_flag,
sc.type = CLOSURE_AMBIENT_OCCLUSION_ID;
sc.data0 = 0.0f;
sc.data1 = 0.0f;
+ sc.data2 = 0.0f;
sc.prim = NULL;
if(sd->num_closure < MAX_CLOSURE) {
@@ -232,6 +231,7 @@ static void flatten_surface_closure_tree(ShaderData *sd, int path_flag,
sc.type = CLOSURE_HOLDOUT_ID;
sc.data0 = 0.0f;
sc.data1 = 0.0f;
+ sc.data2 = 0.0f;
sc.prim = NULL;
if(sd->num_closure < MAX_CLOSURE) {
diff --git a/intern/cycles/kernel/shaders/CMakeLists.txt b/intern/cycles/kernel/shaders/CMakeLists.txt
index 5518d652bf9..0b735ede701 100644
--- a/intern/cycles/kernel/shaders/CMakeLists.txt
+++ b/intern/cycles/kernel/shaders/CMakeLists.txt
@@ -4,6 +4,7 @@
set(SRC_OSL
node_add_closure.osl
node_ambient_occlusion.osl
+ node_anisotropic_bsdf.osl
node_attribute.osl
node_background.osl
node_brick_texture.osl
@@ -13,6 +14,7 @@ set(SRC_OSL
node_checker_texture.osl
node_combine_rgb.osl
node_combine_hsv.osl
+ node_combine_xyz.osl
node_convert_from_color.osl
node_convert_from_float.osl
node_convert_from_int.osl
@@ -57,6 +59,7 @@ set(SRC_OSL
node_rgb_ramp.osl
node_separate_rgb.osl
node_separate_hsv.osl
+ node_separate_xyz.osl
node_set_normal.osl
node_sky_texture.osl
node_subsurface_scattering.osl
@@ -71,7 +74,6 @@ set(SRC_OSL
node_vector_transform.osl
node_velvet_bsdf.osl
node_voronoi_texture.osl
- node_ward_bsdf.osl
node_wavelength.osl
node_blackbody.osl
node_wave_texture.osl
diff --git a/intern/cycles/kernel/shaders/node_ward_bsdf.osl b/intern/cycles/kernel/shaders/node_anisotropic_bsdf.osl
index 2d360d594f2..da1e4f77107 100644
--- a/intern/cycles/kernel/shaders/node_ward_bsdf.osl
+++ b/intern/cycles/kernel/shaders/node_anisotropic_bsdf.osl
@@ -16,8 +16,9 @@
#include "stdosl.h"
-shader node_ward_bsdf(
+shader node_anisotropic_bsdf(
color Color = 0.0,
+ string distribution = "GGX",
float Roughness = 0.0,
float Anisotropy = 0.0,
float Rotation = 0.0,
@@ -44,6 +45,13 @@ shader node_ward_bsdf(
RoughnessV = Roughness / (1.0 - aniso);
}
- BSDF = Color * ward(Normal, T, RoughnessU, RoughnessV);
+ if (distribution == "Sharp")
+ BSDF = Color * reflection(Normal);
+ else if (distribution == "Beckmann")
+ BSDF = Color * microfacet_beckmann_aniso(Normal, T, RoughnessU, RoughnessV);
+ else if (distribution == "GGX")
+ BSDF = Color * microfacet_ggx_aniso(Normal, T, RoughnessU, RoughnessV);
+ else
+ BSDF = Color * ashikhmin_shirley(Normal, T, RoughnessU, RoughnessV);
}
diff --git a/intern/cycles/kernel/shaders/node_checker_texture.osl b/intern/cycles/kernel/shaders/node_checker_texture.osl
index 6723076723c..a6d21fd36f3 100644
--- a/intern/cycles/kernel/shaders/node_checker_texture.osl
+++ b/intern/cycles/kernel/shaders/node_checker_texture.osl
@@ -21,9 +21,9 @@
float checker(point p)
{
- p[0] = (p[0] + 0.00001) * 0.9999;
- p[1] = (p[1] + 0.00001) * 0.9999;
- p[2] = (p[2] + 0.00001) * 0.9999;
+ p[0] = (p[0] + 0.000001) * 0.999999;
+ p[1] = (p[1] + 0.000001) * 0.999999;
+ p[2] = (p[2] + 0.000001) * 0.999999;
int xi = (int)fabs(floor(p[0]));
int yi = (int)fabs(floor(p[1]));
diff --git a/intern/cycles/kernel/shaders/node_combine_xyz.osl b/intern/cycles/kernel/shaders/node_combine_xyz.osl
new file mode 100644
index 00000000000..933dee5bd78
--- /dev/null
+++ b/intern/cycles/kernel/shaders/node_combine_xyz.osl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011-2014 Blender Foundation
+ *
+ * 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
+ */
+
+#include "stdosl.h"
+
+shader node_combine_xyz(
+ float X = 0.0,
+ float Y = 0.0,
+ float Z = 0.0,
+ output vector Vector = 0.8)
+{
+ Vector = vector(X, Y, Z);
+}
+
diff --git a/intern/cycles/kernel/shaders/node_glossy_bsdf.osl b/intern/cycles/kernel/shaders/node_glossy_bsdf.osl
index b4e0fe62223..5c727ca6917 100644
--- a/intern/cycles/kernel/shaders/node_glossy_bsdf.osl
+++ b/intern/cycles/kernel/shaders/node_glossy_bsdf.osl
@@ -19,7 +19,7 @@
shader node_glossy_bsdf(
color Color = 0.8,
- string distribution = "Beckmann",
+ string distribution = "GGX",
float Roughness = 0.2,
normal Normal = N,
output closure color BSDF = 0)
@@ -30,6 +30,8 @@ shader node_glossy_bsdf(
BSDF = Color * microfacet_beckmann(Normal, Roughness);
else if (distribution == "GGX")
BSDF = Color * microfacet_ggx(Normal, Roughness);
+ else
+ BSDF = Color * ashikhmin_shirley(Normal, vector(0, 0, 0), Roughness, Roughness);
}
diff --git a/intern/cycles/kernel/shaders/node_separate_xyz.osl b/intern/cycles/kernel/shaders/node_separate_xyz.osl
new file mode 100644
index 00000000000..63725cb9995
--- /dev/null
+++ b/intern/cycles/kernel/shaders/node_separate_xyz.osl
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * 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
+ */
+
+#include "stdosl.h"
+
+shader node_separate_xyz(
+ vector Vector = 0.8,
+ output float X = 0.0,
+ output float Y = 0.0,
+ output float Z = 0.0)
+{
+ X = Vector[0];
+ Y = Vector[1];
+ Z = Vector[2];
+}
diff --git a/intern/cycles/kernel/shaders/stdosl.h b/intern/cycles/kernel/shaders/stdosl.h
index 6f824ea8ebd..e39db8097f2 100644
--- a/intern/cycles/kernel/shaders/stdosl.h
+++ b/intern/cycles/kernel/shaders/stdosl.h
@@ -483,10 +483,12 @@ closure color reflection(normal N) BUILTIN;
closure color refraction(normal N, float eta) BUILTIN;
closure color transparent() BUILTIN;
closure color microfacet_ggx(normal N, float ag) BUILTIN;
+closure color microfacet_ggx_aniso(normal N, vector T, float ax, float ay) BUILTIN;
closure color microfacet_ggx_refraction(normal N, float ag, float eta) BUILTIN;
closure color microfacet_beckmann(normal N, float ab) BUILTIN;
+closure color microfacet_beckmann_aniso(normal N, vector T, float ax, float ay) BUILTIN;
closure color microfacet_beckmann_refraction(normal N, float ab, float eta) BUILTIN;
-closure color ward(normal N, vector T,float ax, float ay) BUILTIN;
+closure color ashikhmin_shirley(normal N, vector T,float ax, float ay) BUILTIN;
closure color ashikhmin_velvet(normal N, float sigma) BUILTIN;
closure color emission() BUILTIN;
closure color background() BUILTIN;
diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h
index 6d556a66afa..d6663aae9db 100644
--- a/intern/cycles/kernel/svm/svm.h
+++ b/intern/cycles/kernel/svm/svm.h
@@ -167,8 +167,8 @@ CCL_NAMESPACE_END
#include "svm_math.h"
#include "svm_mix.h"
#include "svm_ramp.h"
-#include "svm_sepcomb_rgb.h"
#include "svm_sepcomb_hsv.h"
+#include "svm_sepcomb_vector.h"
#include "svm_musgrave.h"
#include "svm_sky.h"
#include "svm_tex_coord.h"
@@ -327,11 +327,11 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, Shade
case NODE_MIX:
svm_node_mix(kg, sd, stack, node.y, node.z, node.w, &offset);
break;
- case NODE_SEPARATE_RGB:
- svm_node_separate_rgb(sd, stack, node.y, node.z, node.w);
+ case NODE_SEPARATE_VECTOR:
+ svm_node_separate_vector(sd, stack, node.y, node.z, node.w);
break;
- case NODE_COMBINE_RGB:
- svm_node_combine_rgb(sd, stack, node.y, node.z, node.w);
+ case NODE_COMBINE_VECTOR:
+ svm_node_combine_vector(sd, stack, node.y, node.z, node.w);
break;
case NODE_SEPARATE_HSV:
svm_node_separate_hsv(kg, sd, stack, node.y, node.z, node.w, &offset);
diff --git a/intern/cycles/kernel/svm/svm_blackbody.h b/intern/cycles/kernel/svm/svm_blackbody.h
index 63dbf27d35e..15257aed92e 100644
--- a/intern/cycles/kernel/svm/svm_blackbody.h
+++ b/intern/cycles/kernel/svm/svm_blackbody.h
@@ -55,7 +55,7 @@ ccl_device void svm_node_blackbody(KernelGlobals *kg, ShaderData *sd, float *sta
just one (the OSL-lerp is also automatically done for us by "lookup_table_read") */
float t = powf((temperature - BB_DRAPPER) * (1.0f / BB_TABLE_SPACING), (1.0f / BB_TABLE_XPOWER));
- int blackbody_table_offset = kernel_data.blackbody.table_offset;
+ int blackbody_table_offset = kernel_data.tables.blackbody_offset;
/* Retrieve colors from the lookup table */
float lutval = t*lookuptablenormalize;
diff --git a/intern/cycles/kernel/svm/svm_checker.h b/intern/cycles/kernel/svm/svm_checker.h
index 8d1a1a40449..e0408ad334a 100644
--- a/intern/cycles/kernel/svm/svm_checker.h
+++ b/intern/cycles/kernel/svm/svm_checker.h
@@ -21,9 +21,9 @@ CCL_NAMESPACE_BEGIN
ccl_device_noinline float svm_checker(float3 p)
{
/* avoid precision issues on unit coordinates */
- p.x = (p.x + 0.00001f)*0.9999f;
- p.y = (p.y + 0.00001f)*0.9999f;
- p.z = (p.z + 0.00001f)*0.9999f;
+ p.x = (p.x + 0.000001f)*0.999999f;
+ p.y = (p.y + 0.000001f)*0.999999f;
+ p.z = (p.z + 0.000001f)*0.999999f;
int xi = float_to_int(fabsf(floorf(p.x)));
int yi = float_to_int(fabsf(floorf(p.y)));
diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/svm_closure.h
index 27c5a19a7a0..5fcc44e478b 100644
--- a/intern/cycles/kernel/svm/svm_closure.h
+++ b/intern/cycles/kernel/svm/svm_closure.h
@@ -24,6 +24,7 @@ ccl_device void svm_node_glass_setup(ShaderData *sd, ShaderClosure *sc, int type
if(refract) {
sc->data0 = eta;
sc->data1 = 0.0f;
+ sc->data2 = 0.0f;
sd->flag |= bsdf_refraction_setup(sc);
}
else
@@ -31,7 +32,8 @@ ccl_device void svm_node_glass_setup(ShaderData *sd, ShaderClosure *sc, int type
}
else if(type == CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID) {
sc->data0 = roughness;
- sc->data1 = eta;
+ sc->data1 = roughness;
+ sc->data2 = eta;
if(refract)
sd->flag |= bsdf_microfacet_beckmann_refraction_setup(sc);
@@ -40,7 +42,8 @@ ccl_device void svm_node_glass_setup(ShaderData *sd, ShaderClosure *sc, int type
}
else {
sc->data0 = roughness;
- sc->data1 = eta;
+ sc->data1 = roughness;
+ sc->data2 = eta;
if(refract)
sd->flag |= bsdf_microfacet_ggx_refraction_setup(sc);
@@ -135,11 +138,13 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
if(roughness == 0.0f) {
sc->data0 = 0.0f;
sc->data1 = 0.0f;
+ sc->data2 = 0.0f;
sd->flag |= bsdf_diffuse_setup(sc);
}
else {
sc->data0 = roughness;
sc->data1 = 0.0f;
+ sc->data2 = 0.0f;
sd->flag |= bsdf_oren_nayar_setup(sc);
}
}
@@ -151,6 +156,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
if(sc) {
sc->data0 = 0.0f;
sc->data1 = 0.0f;
+ sc->data2 = 0.0f;
sc->N = N;
sd->flag |= bsdf_translucent_setup(sc);
}
@@ -162,6 +168,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
if(sc) {
sc->data0 = 0.0f;
sc->data1 = 0.0f;
+ sc->data2 = 0.0f;
sc->N = N;
sd->flag |= bsdf_transparent_setup(sc);
}
@@ -169,7 +176,8 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
}
case CLOSURE_BSDF_REFLECTION_ID:
case CLOSURE_BSDF_MICROFACET_GGX_ID:
- case CLOSURE_BSDF_MICROFACET_BECKMANN_ID: {
+ case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
+ case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID: {
#ifdef __CAUSTICS_TRICKS__
if(kernel_data.integrator.no_caustics && (path_flag & PATH_RAY_DIFFUSE))
break;
@@ -179,15 +187,18 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
if(sc) {
sc->N = N;
sc->data0 = param1;
- sc->data1 = 0.0f;
+ sc->data1 = param1;
+ sc->data2 = 0.0f;
/* setup bsdf */
if(type == CLOSURE_BSDF_REFLECTION_ID)
sd->flag |= bsdf_reflection_setup(sc);
else if(type == CLOSURE_BSDF_MICROFACET_BECKMANN_ID)
sd->flag |= bsdf_microfacet_beckmann_setup(sc);
- else
+ else if(type == CLOSURE_BSDF_MICROFACET_GGX_ID)
sd->flag |= bsdf_microfacet_ggx_setup(sc);
+ else
+ sd->flag |= bsdf_ashikhmin_shirley_setup(sc);
}
break;
@@ -203,18 +214,28 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
if(sc) {
sc->N = N;
- sc->data0 = param1;
float eta = fmaxf(param2, 1e-5f);
- sc->data1 = (sd->flag & SD_BACKFACING)? 1.0f/eta: eta;
+ eta = (sd->flag & SD_BACKFACING)? 1.0f/eta: eta;
/* setup bsdf */
- if(type == CLOSURE_BSDF_REFRACTION_ID)
+ if(type == CLOSURE_BSDF_REFRACTION_ID) {
+ sc->data0 = eta;
+ sc->data1 = 0.0f;
+ sc->data2 = 0.0f;
+
sd->flag |= bsdf_refraction_setup(sc);
- else if(type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID)
- sd->flag |= bsdf_microfacet_beckmann_refraction_setup(sc);
- else
- sd->flag |= bsdf_microfacet_ggx_refraction_setup(sc);
+ }
+ else {
+ sc->data0 = param1;
+ sc->data1 = param1;
+ sc->data2 = eta;
+
+ if(type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID)
+ sd->flag |= bsdf_microfacet_beckmann_refraction_setup(sc);
+ else
+ sd->flag |= bsdf_microfacet_ggx_refraction_setup(sc);
+ }
}
break;
@@ -261,7 +282,9 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
break;
}
- case CLOSURE_BSDF_WARD_ID: {
+ case CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID:
+ case CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID:
+ case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID: {
#ifdef __CAUSTICS_TRICKS__
if(kernel_data.integrator.no_caustics && (path_flag & PATH_RAY_DIFFUSE))
break;
@@ -293,7 +316,14 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
sc->data1 = roughness/(1.0f - anisotropy);
}
- sd->flag |= bsdf_ward_setup(sc);
+ sc->data2 = 0.0f;
+
+ if (type == CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID)
+ sd->flag |= bsdf_microfacet_beckmann_aniso_setup(sc);
+ else if (type == CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID)
+ sd->flag |= bsdf_microfacet_ggx_aniso_setup(sc);
+ else
+ sd->flag |= bsdf_ashikhmin_shirley_aniso_setup(sc);
#else
sd->flag |= bsdf_diffuse_setup(sc);
#endif
@@ -309,6 +339,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
/* sigma */
sc->data0 = clamp(param1, 0.0f, 1.0f);
sc->data1 = 0.0f;
+ sc->data2 = 0.0f;
sd->flag |= bsdf_ashikhmin_velvet_setup(sc);
}
break;
@@ -322,6 +353,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
sc->N = N;
sc->data0 = param1;
sc->data1 = param2;
+ sc->data2 = 0.0f;
if (type == CLOSURE_BSDF_DIFFUSE_TOON_ID)
sd->flag |= bsdf_diffuse_toon_setup(sc);
@@ -356,11 +388,11 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
sc->N = N;
sc->data0 = param1;
sc->data1 = param2;
- sc->offset = -stack_load_float(stack, data_node.z);
+ sc->data2 = -stack_load_float(stack, data_node.z);
if(!(sd->type & PRIMITIVE_ALL_CURVE)) {
sc->T = normalize(sd->dPdv);
- sc->offset = 0.0f;
+ sc->data2 = 0.0f;
}
else
sc->T = sd->dPdu;
@@ -405,6 +437,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
sc->sample_weight = sample_weight;
sc->data0 = radius.x;
sc->data1 = texture_blur;
+ sc->data2 = 0.0f;
sc->T.x = sharpness;
#ifdef __OSL__
sc->prim = NULL;
@@ -421,6 +454,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
sc->sample_weight = sample_weight;
sc->data0 = radius.y;
sc->data1 = texture_blur;
+ sc->data2 = 0.0f;
sc->T.x = sharpness;
#ifdef __OSL__
sc->prim = NULL;
@@ -437,6 +471,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
sc->sample_weight = sample_weight;
sc->data0 = radius.z;
sc->data1 = texture_blur;
+ sc->data2 = 0.0f;
sc->T.x = sharpness;
#ifdef __OSL__
sc->prim = NULL;
diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/svm_image.h
index daf7c6652d2..a7abeda18e5 100644
--- a/intern/cycles/kernel/svm/svm_image.h
+++ b/intern/cycles/kernel/svm/svm_image.h
@@ -134,8 +134,8 @@ ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y,
{
#ifdef __KERNEL_CPU__
#ifdef __KERNEL_SSE2__
- __m128 r_m128;
- float4 &r = (float4 &)r_m128;
+ ssef r_ssef;
+ float4 &r = (float4 &)r_ssef;
r = kernel_tex_image_interp(id, x, y);
#else
float4 r = kernel_tex_image_interp(id, x, y);
@@ -252,9 +252,9 @@ ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y,
case 96: r = kernel_tex_image_interp(__tex_image_096, x, y); break;
case 97: r = kernel_tex_image_interp(__tex_image_097, x, y); break;
case 98: r = kernel_tex_image_interp(__tex_image_098, x, y); break;
- case 99: r = kernel_tex_image_interp(__tex_image_099, x, y); break;
#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 300)
+ case 99: r = kernel_tex_image_interp(__tex_image_099, x, y); break;
case 100: r = kernel_tex_image_interp(__tex_image_100, x, y); break;
case 101: r = kernel_tex_image_interp(__tex_image_101, x, y); break;
case 102: r = kernel_tex_image_interp(__tex_image_102, x, y); break;
@@ -318,14 +318,14 @@ ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y,
float alpha = r.w;
if(use_alpha && alpha != 1.0f && alpha != 0.0f) {
- r_m128 = _mm_div_ps(r_m128, _mm_set1_ps(alpha));
+ r_ssef = r_ssef / ssef(alpha);
if(id >= TEX_NUM_FLOAT_IMAGES)
- r_m128 = _mm_min_ps(r_m128, _mm_set1_ps(1.0f));
+ r_ssef = min(r_ssef, ssef(1.0f));
r.w = alpha;
}
if(srgb) {
- r_m128 = color_srgb_to_scene_linear(r_m128);
+ r_ssef = color_srgb_to_scene_linear(r_ssef);
r.w = alpha;
}
#else
diff --git a/intern/cycles/kernel/svm/svm_noise.h b/intern/cycles/kernel/svm/svm_noise.h
index 91dda8972f9..869341c81f4 100644
--- a/intern/cycles/kernel/svm/svm_noise.h
+++ b/intern/cycles/kernel/svm/svm_noise.h
@@ -38,11 +38,11 @@ ccl_device int quick_floor(float x)
return float_to_int(x) - ((x < 0) ? 1 : 0);
}
#else
-ccl_device_inline __m128i quick_floor_sse(const __m128& x)
+ccl_device_inline ssei quick_floor_sse(const ssef& x)
{
- __m128i b = _mm_cvttps_epi32(x);
- __m128i isneg = _mm_castps_si128(_mm_cmplt_ps(x, _mm_set1_ps(0.0f)));
- return _mm_add_epi32(b, isneg); // unsaturated add 0xffffffff is the same as subtract -1
+ ssei b = truncatei(x);
+ ssei isneg = cast((x < ssef(0.0f)).m128);
+ return b + isneg; // unsaturated add 0xffffffff is the same as subtract -1
}
#endif
@@ -52,9 +52,9 @@ ccl_device float bits_to_01(uint bits)
return bits * (1.0f/(float)0xFFFFFFFF);
}
#else
-ccl_device_inline __m128 bits_to_01_sse(const __m128i& bits)
+ccl_device_inline ssef bits_to_01_sse(const ssei& bits)
{
- return _mm_mul_ps(uint32_to_float(bits), _mm_set1_ps(1.0f/(float)0xFFFFFFFF));
+ return uint32_to_float(bits) * ssef(1.0f/(float)0xFFFFFFFF);
}
#endif
@@ -88,16 +88,16 @@ ccl_device uint hash(uint kx, uint ky, uint kz)
}
#ifdef __KERNEL_SSE2__
-ccl_device_inline __m128i hash_sse(const __m128i& kx, const __m128i& ky, const __m128i& kz)
+ccl_device_inline ssei hash_sse(const ssei& kx, const ssei& ky, const ssei& kz)
{
-#define rot(x,k) _mm_or_si128(_mm_slli_epi32((x), (k)), _mm_srli_epi32((x), 32-(k)))
-#define xor_rot(a, b, c) do {a = _mm_xor_si128(a, b); a = _mm_sub_epi32(a, rot(b, c));} while(0)
+#define rot(x,k) (((x)<<(k)) | (srl(x, 32-(k))))
+#define xor_rot(a, b, c) do {a = a^b; a = a - rot(b, c);} while(0)
uint len = 3;
- __m128i magic = _mm_set1_epi32(0xdeadbeef + (len << 2) + 13);
- __m128i a = _mm_add_epi32(magic, kx);
- __m128i b = _mm_add_epi32(magic, ky);
- __m128i c = _mm_add_epi32(magic, kz);
+ ssei magic = ssei(0xdeadbeef + (len << 2) + 13);
+ ssei a = magic + kx;
+ ssei b = magic + ky;
+ ssei c = magic + kz;
xor_rot(c, b, 14);
xor_rot(a, c, 11);
@@ -133,10 +133,10 @@ ccl_device float floorfrac(float x, int* i)
return x - *i;
}
#else
-ccl_device_inline __m128 floorfrac_sse(const __m128& x, __m128i *i)
+ccl_device_inline ssef floorfrac_sse(const ssef& x, ssei *i)
{
*i = quick_floor_sse(x);
- return _mm_sub_ps(x, _mm_cvtepi32_ps(*i));
+ return x - ssef(*i);
}
#endif
@@ -146,11 +146,11 @@ ccl_device float fade(float t)
return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
}
#else
-ccl_device_inline __m128 fade_sse(const __m128 *t)
+ccl_device_inline ssef fade_sse(const ssef *t)
{
- __m128 a = fma(*t, _mm_set1_ps(6.0f), _mm_set1_ps(-15.0f));
- __m128 b = fma(*t, a, _mm_set1_ps(10.0f));
- return _mm_mul_ps(_mm_mul_ps(*t, *t), _mm_mul_ps(*t, b));
+ ssef a = madd(*t, ssef(6.0f), ssef(-15.0f));
+ ssef b = madd(*t, a, ssef(10.0f));
+ return ((*t) * (*t)) * ((*t) * b);
}
#endif
@@ -160,10 +160,10 @@ ccl_device float nerp(float t, float a, float b)
return (1.0f - t) * a + t * b;
}
#else
-ccl_device_inline __m128 nerp_sse(const __m128& t, const __m128& a, const __m128& b)
+ccl_device_inline ssef nerp_sse(const ssef& t, const ssef& a, const ssef& b)
{
- __m128 x1 = _mm_mul_ps(_mm_sub_ps(_mm_set1_ps(1.0f), t), a);
- return fma(t, b, x1);
+ ssef x1 = (ssef(1.0f) - t) * a;
+ return madd(t, b, x1);
}
#endif
@@ -178,35 +178,35 @@ ccl_device float grad(int hash, float x, float y, float z)
return ((h&1) ? -u : u) + ((h&2) ? -v : v);
}
#else
-ccl_device_inline __m128 grad_sse(const __m128i& hash, const __m128& x, const __m128& y, const __m128& z)
+ccl_device_inline ssef grad_sse(const ssei& hash, const ssef& x, const ssef& y, const ssef& z)
{
- __m128i c1 = _mm_set1_epi32(1);
- __m128i c2 = _mm_set1_epi32(2);
+ ssei c1 = ssei(1);
+ ssei c2 = ssei(2);
- __m128i h = _mm_and_si128(hash, _mm_set1_epi32(15)); // h = hash & 15
+ ssei h = hash & ssei(15); // h = hash & 15
- __m128i case_ux = _mm_cmplt_epi32(h, _mm_set1_epi32(8)); // 0xffffffff if h < 8 else 0
+ sseb case_ux = h < ssei(8); // 0xffffffff if h < 8 else 0
- __m128 u = blend(_mm_castsi128_ps(case_ux), x, y); // u = h<8 ? x : y
+ ssef u = select(case_ux, x, y); // u = h<8 ? x : y
- __m128i case_vy = _mm_cmplt_epi32(h, _mm_set1_epi32(4)); // 0xffffffff if h < 4 else 0
+ sseb case_vy = h < ssei(4); // 0xffffffff if h < 4 else 0
- __m128i case_h12 = _mm_cmpeq_epi32(h, _mm_set1_epi32(12)); // 0xffffffff if h == 12 else 0
- __m128i case_h14 = _mm_cmpeq_epi32(h, _mm_set1_epi32(14)); // 0xffffffff if h == 14 else 0
+ sseb case_h12 = h == ssei(12); // 0xffffffff if h == 12 else 0
+ sseb case_h14 = h == ssei(14); // 0xffffffff if h == 14 else 0
- __m128i case_vx = _mm_or_si128(case_h12, case_h14); // 0xffffffff if h == 12 or h == 14 else 0
+ sseb case_vx = case_h12 | case_h14; // 0xffffffff if h == 12 or h == 14 else 0
- __m128 v = blend(_mm_castsi128_ps(case_vy), y, blend(_mm_castsi128_ps(case_vx), x, z)); // v = h<4 ? y : h == 12 || h == 14 ? x : z
+ ssef v = select(case_vy, y, select(case_vx, x, z)); // v = h<4 ? y : h == 12 || h == 14 ? x : z
- __m128i case_uneg = _mm_slli_epi32(_mm_and_si128(h, c1), 31); // 1<<31 if h&1 else 0
- __m128 case_uneg_mask = _mm_castsi128_ps(case_uneg); // -0.0 if h&1 else +0.0
- __m128 ru = _mm_xor_ps(u, case_uneg_mask); // -u if h&1 else u (copy float sign)
+ ssei case_uneg = (h & c1) << 31; // 1<<31 if h&1 else 0
+ ssef case_uneg_mask = cast(case_uneg); // -0.0 if h&1 else +0.0
+ ssef ru = u ^ case_uneg_mask; // -u if h&1 else u (copy float sign)
- __m128i case_vneg = _mm_slli_epi32(_mm_and_si128(h, c2), 30); // 2<<30 if h&2 else 0
- __m128 case_vneg_mask = _mm_castsi128_ps(case_vneg); // -0.0 if h&2 else +0.0
- __m128 rv = _mm_xor_ps(v, case_vneg_mask); // -v if h&2 else v (copy float sign)
+ ssei case_vneg = (h & c2) << 30; // 2<<30 if h&2 else 0
+ ssef case_vneg_mask = cast(case_vneg); // -0.0 if h&2 else +0.0
+ ssef rv = v ^ case_vneg_mask; // -v if h&2 else v (copy float sign)
- __m128 r = _mm_add_ps(ru, rv); // ((h&1) ? -u : u) + ((h&2) ? -v : v)
+ ssef r = ru + rv; // ((h&1) ? -u : u) + ((h&2) ? -v : v)
return r;
}
#endif
@@ -217,9 +217,9 @@ ccl_device float scale3(float result)
return 0.9820f * result;
}
#else
-ccl_device_inline __m128 scale3_sse(const __m128& result)
+ccl_device_inline ssef scale3_sse(const ssef& result)
{
- return _mm_mul_ps(_mm_set1_ps(0.9820f), result);
+ return ssef(0.9820f) * result;
}
#endif
@@ -252,41 +252,41 @@ ccl_device_noinline float perlin(float x, float y, float z)
#else
ccl_device_noinline float perlin(float x, float y, float z)
{
- __m128 xyz = _mm_setr_ps(x, y, z, 0.0f);
- __m128i XYZ;
+ ssef xyz = ssef(x, y, z, 0.0f);
+ ssei XYZ;
- __m128 fxyz = floorfrac_sse(xyz, &XYZ);
+ ssef fxyz = floorfrac_sse(xyz, &XYZ);
- __m128 uvw = fade_sse(&fxyz);
- __m128 u = broadcast<0>(uvw), v = broadcast<1>(uvw), w = broadcast<2>(uvw);
+ ssef uvw = fade_sse(&fxyz);
+ ssef u = shuffle<0>(uvw), v = shuffle<1>(uvw), w = shuffle<2>(uvw);
- __m128i XYZ_ofc = _mm_add_epi32(XYZ, _mm_set1_epi32(1));
- __m128i vdy = shuffle<1, 1, 1, 1>(XYZ, XYZ_ofc); // +0, +0, +1, +1
- __m128i vdz = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(XYZ, XYZ_ofc)); // +0, +1, +0, +1
+ ssei XYZ_ofc = XYZ + ssei(1);
+ ssei vdy = shuffle<1, 1, 1, 1>(XYZ, XYZ_ofc); // +0, +0, +1, +1
+ ssei vdz = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(XYZ, XYZ_ofc)); // +0, +1, +0, +1
- __m128i h1 = hash_sse(broadcast<0>(XYZ), vdy, vdz); // hash directions 000, 001, 010, 011
- __m128i h2 = hash_sse(broadcast<0>(XYZ_ofc), vdy, vdz); // hash directions 100, 101, 110, 111
+ ssei h1 = hash_sse(shuffle<0>(XYZ), vdy, vdz); // hash directions 000, 001, 010, 011
+ ssei h2 = hash_sse(shuffle<0>(XYZ_ofc), vdy, vdz); // hash directions 100, 101, 110, 111
- __m128 fxyz_ofc = _mm_sub_ps(fxyz, _mm_set1_ps(1.0f));
- __m128 vfy = shuffle<1, 1, 1, 1>(fxyz, fxyz_ofc);
- __m128 vfz = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(fxyz, fxyz_ofc));
+ ssef fxyz_ofc = fxyz - ssef(1.0f);
+ ssef vfy = shuffle<1, 1, 1, 1>(fxyz, fxyz_ofc);
+ ssef vfz = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(fxyz, fxyz_ofc));
- __m128 g1 = grad_sse(h1, broadcast<0>(fxyz), vfy, vfz);
- __m128 g2 = grad_sse(h2, broadcast<0>(fxyz_ofc), vfy, vfz);
- __m128 n1 = nerp_sse(u, g1, g2);
+ ssef g1 = grad_sse(h1, shuffle<0>(fxyz), vfy, vfz);
+ ssef g2 = grad_sse(h2, shuffle<0>(fxyz_ofc), vfy, vfz);
+ ssef n1 = nerp_sse(u, g1, g2);
- __m128 n1_half = shuffle<2, 3, 2, 3>(n1); // extract 2 floats to a separate vector
- __m128 n2 = nerp_sse(v, n1, n1_half); // process nerp([a, b, _, _], [c, d, _, _]) -> [a', b', _, _]
+ ssef n1_half = shuffle<2, 3, 2, 3>(n1); // extract 2 floats to a separate vector
+ ssef n2 = nerp_sse(v, n1, n1_half); // process nerp([a, b, _, _], [c, d, _, _]) -> [a', b', _, _]
- __m128 n2_second = broadcast<1>(n2); // extract b to a separate vector
- __m128 result = nerp_sse(w, n2, n2_second); // process nerp([a', _, _, _], [b', _, _, _]) -> [a'', _, _, _]
+ ssef n2_second = shuffle<1>(n2); // extract b to a separate vector
+ ssef result = nerp_sse(w, n2, n2_second); // process nerp([a', _, _, _], [b', _, _, _]) -> [a'', _, _, _]
- __m128 r = scale3_sse(result);
+ ssef r = scale3_sse(result);
- __m128 infmask = _mm_castsi128_ps(_mm_set1_epi32(0x7f800000));
- __m128 rinfmask = _mm_cmpeq_ps(_mm_and_ps(r, infmask), infmask); // 0xffffffff if r is inf/-inf/nan else 0
- __m128 rfinite = _mm_andnot_ps(rinfmask, r); // 0 if r is inf/-inf/nan else r
- return _mm_cvtss_f32(rfinite);
+ ssef infmask = cast(ssei(0x7f800000));
+ ssef rinfmask = ((r & infmask) == infmask).m128; // 0xffffffff if r is inf/-inf/nan else 0
+ ssef rfinite = andnot(rinfmask, r); // 0 if r is inf/-inf/nan else r
+ return extract<0>(rfinite);
}
#endif
@@ -357,12 +357,12 @@ ccl_device float3 cellnoise_color(float3 p)
return make_float3(r, g, b);
}
#else
-ccl_device __m128 cellnoise_color(const __m128& p)
+ccl_device ssef cellnoise_color(const ssef& p)
{
- __m128i ip = quick_floor_sse(p);
- __m128i ip_yxz = shuffle<1, 0, 2, 3>(ip);
- __m128i ip_xyy = shuffle<0, 1, 1, 3>(ip);
- __m128i ip_zzx = shuffle<2, 2, 0, 3>(ip);
+ ssei ip = quick_floor_sse(p);
+ ssei ip_yxz = shuffle<1, 0, 2, 3>(ip);
+ ssei ip_xyy = shuffle<0, 1, 1, 3>(ip);
+ ssei ip_zzx = shuffle<2, 2, 0, 3>(ip);
return bits_to_01_sse(hash_sse(ip_xyy, ip_yxz, ip_zzx));
}
#endif
diff --git a/intern/cycles/kernel/svm/svm_sepcomb_rgb.h b/intern/cycles/kernel/svm/svm_sepcomb_rgb.h
deleted file mode 100644
index 34c4449ecdb..00000000000
--- a/intern/cycles/kernel/svm/svm_sepcomb_rgb.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2011-2013 Blender Foundation
- *
- * 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
- */
-
-CCL_NAMESPACE_BEGIN
-
-ccl_device void svm_node_combine_rgb(ShaderData *sd, float *stack, uint in_offset, uint color_index, uint out_offset)
-{
- float color = stack_load_float(stack, in_offset);
-
- if (stack_valid(out_offset))
- stack_store_float(stack, out_offset+color_index, color);
-}
-
-ccl_device void svm_node_separate_rgb(ShaderData *sd, float *stack, uint icolor_offset, uint color_index, uint out_offset)
-{
- float3 color = stack_load_float3(stack, icolor_offset);
-
- if (stack_valid(out_offset)) {
- if (color_index == 0)
- stack_store_float(stack, out_offset, color.x);
- else if (color_index == 1)
- stack_store_float(stack, out_offset, color.y);
- else
- stack_store_float(stack, out_offset, color.z);
- }
-}
-
-CCL_NAMESPACE_END
-
diff --git a/intern/cycles/kernel/svm/svm_sepcomb_vector.h b/intern/cycles/kernel/svm/svm_sepcomb_vector.h
new file mode 100644
index 00000000000..c8e7e34f87d
--- /dev/null
+++ b/intern/cycles/kernel/svm/svm_sepcomb_vector.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2011-2014 Blender Foundation
+ *
+ * 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
+ */
+
+CCL_NAMESPACE_BEGIN
+
+/* Vector combine / separate, used for the RGB and XYZ nodes */
+
+ccl_device void svm_node_combine_vector(ShaderData *sd, float *stack, uint in_offset, uint vector_index, uint out_offset)
+{
+ float vector = stack_load_float(stack, in_offset);
+
+ if (stack_valid(out_offset))
+ stack_store_float(stack, out_offset+vector_index, vector);
+}
+
+ccl_device void svm_node_separate_vector(ShaderData *sd, float *stack, uint ivector_offset, uint vector_index, uint out_offset)
+{
+ float3 vector = stack_load_float3(stack, ivector_offset);
+
+ if (stack_valid(out_offset)) {
+ if (vector_index == 0)
+ stack_store_float(stack, out_offset, vector.x);
+ else if (vector_index == 1)
+ stack_store_float(stack, out_offset, vector.y);
+ else
+ stack_store_float(stack, out_offset, vector.z);
+ }
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/kernel/svm/svm_texture.h b/intern/cycles/kernel/svm/svm_texture.h
index 5fd9204cbf6..d97c85db36a 100644
--- a/intern/cycles/kernel/svm/svm_texture.h
+++ b/intern/cycles/kernel/svm/svm_texture.h
@@ -140,15 +140,15 @@ ccl_device float voronoi_F1_distance(float3 p)
}
}
#else
- __m128 vec_p = load_m128(p);
- __m128i xyzi = quick_floor_sse(vec_p);
+ ssef vec_p = load4f(p);
+ ssei xyzi = quick_floor_sse(vec_p);
for (int xx = -1; xx <= 1; xx++) {
for (int yy = -1; yy <= 1; yy++) {
for (int zz = -1; zz <= 1; zz++) {
- __m128 ip = _mm_cvtepi32_ps(_mm_add_epi32(xyzi, _mm_setr_epi32(xx, yy, zz, 0)));
- __m128 vp = _mm_add_ps(ip, cellnoise_color(ip));
- float d = len_squared<1, 1, 1, 0>(_mm_sub_ps(vec_p, vp));
+ ssef ip = ssef(xyzi + ssei(xx, yy, zz, 0));
+ ssef vp = ip + cellnoise_color(ip);
+ float d = len_squared<1, 1, 1, 0>(vec_p - vp);
da = min(d, da);
}
}
@@ -184,15 +184,15 @@ ccl_device float3 voronoi_F1_color(float3 p)
return cellnoise_color(pa);
#else
- __m128 pa, vec_p = load_m128(p);
- __m128i xyzi = quick_floor_sse(vec_p);
+ ssef pa, vec_p = load4f(p);
+ ssei xyzi = quick_floor_sse(vec_p);
for (int xx = -1; xx <= 1; xx++) {
for (int yy = -1; yy <= 1; yy++) {
for (int zz = -1; zz <= 1; zz++) {
- __m128 ip = _mm_cvtepi32_ps(_mm_add_epi32(xyzi, _mm_setr_epi32(xx, yy, zz, 0)));
- __m128 vp = _mm_add_ps(ip, cellnoise_color(ip));
- float d = len_squared<1, 1, 1, 0>(_mm_sub_ps(vec_p, vp));
+ ssef ip = ssef(xyzi + ssei(xx, yy, zz, 0));
+ ssef vp = ip + cellnoise_color(ip);
+ float d = len_squared<1, 1, 1, 0>(vec_p - vp);
if(d < da) {
da = d;
@@ -202,7 +202,7 @@ ccl_device float3 voronoi_F1_color(float3 p)
}
}
- __m128 color = cellnoise_color(pa);
+ ssef color = cellnoise_color(pa);
return (float3 &)color;
#endif
}
diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h
index fda035fdbe0..103f562ed60 100644
--- a/intern/cycles/kernel/svm/svm_types.h
+++ b/intern/cycles/kernel/svm/svm_types.h
@@ -78,8 +78,8 @@ typedef enum NodeType {
NODE_CLOSURE_HOLDOUT,
NODE_LAYER_WEIGHT,
NODE_CLOSURE_VOLUME,
- NODE_SEPARATE_RGB,
- NODE_COMBINE_RGB,
+ NODE_SEPARATE_VECTOR,
+ NODE_COMBINE_VECTOR,
NODE_SEPARATE_HSV,
NODE_COMBINE_HSV,
NODE_HSV,
@@ -357,7 +357,10 @@ typedef enum ClosureType {
CLOSURE_BSDF_REFLECTION_ID,
CLOSURE_BSDF_MICROFACET_GGX_ID,
CLOSURE_BSDF_MICROFACET_BECKMANN_ID,
- CLOSURE_BSDF_WARD_ID,
+ CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID,
+ CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID,
+ CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID,
+ CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID,
CLOSURE_BSDF_ASHIKHMIN_VELVET_ID,
CLOSURE_BSDF_WESTIN_BACKSCATTER_ID,
CLOSURE_BSDF_PHONG_RAMP_ID,
@@ -403,7 +406,7 @@ typedef enum ClosureType {
#define CLOSURE_IS_BSDF_GLOSSY(type) (type >= CLOSURE_BSDF_GLOSSY_ID && type <= CLOSURE_BSDF_HAIR_REFLECTION_ID)
#define CLOSURE_IS_BSDF_TRANSMISSION(type) (type >= CLOSURE_BSDF_TRANSMISSION_ID && type <= CLOSURE_BSDF_HAIR_TRANSMISSION_ID)
#define CLOSURE_IS_BSDF_BSSRDF(type) (type == CLOSURE_BSDF_BSSRDF_ID)
-#define CLOSURE_IS_BSDF_ANISOTROPIC(type) (type == CLOSURE_BSDF_WARD_ID)
+#define CLOSURE_IS_BSDF_ANISOTROPIC(type) (type >= CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID && type <= CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID)
#define CLOSURE_IS_BSDF_OR_BSSRDF(type) (type <= CLOSURE_BSSRDF_GAUSSIAN_ID)
#define CLOSURE_IS_BSSRDF(type) (type >= CLOSURE_BSSRDF_CUBIC_ID && type <= CLOSURE_BSSRDF_GAUSSIAN_ID)
#define CLOSURE_IS_VOLUME(type) (type >= CLOSURE_VOLUME_ID && type <= CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID)
diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp
index 72781bb0f9b..8abf869a775 100644
--- a/intern/cycles/render/attribute.cpp
+++ b/intern/cycles/render/attribute.cpp
@@ -69,6 +69,15 @@ void Attribute::add(const float& f)
buffer.push_back(data[i]);
}
+void Attribute::add(const uchar4& f)
+{
+ char *data = (char*)&f;
+ size_t size = sizeof(f);
+
+ for(size_t i = 0; i < size; i++)
+ buffer.push_back(data[i]);
+}
+
void Attribute::add(const float3& f)
{
char *data = (char*)&f;
@@ -136,6 +145,7 @@ size_t Attribute::element_size(int numverts, int numtris, int numsteps, int numc
size = numtris;
break;
case ATTR_ELEMENT_CORNER:
+ case ATTR_ELEMENT_CORNER_BYTE:
size = numtris*3;
break;
case ATTR_ELEMENT_CURVE:
diff --git a/intern/cycles/render/attribute.h b/intern/cycles/render/attribute.h
index 9fc32db8444..f5227ebde52 100644
--- a/intern/cycles/render/attribute.h
+++ b/intern/cycles/render/attribute.h
@@ -68,6 +68,7 @@ public:
float3 *data_float3() { return (float3*)data(); }
float4 *data_float4() { return (float4*)data(); }
float *data_float() { return (float*)data(); }
+ uchar4 *data_uchar4() { return (uchar4*)data(); }
Transform *data_transform() { return (Transform*)data(); }
VoxelAttribute *data_voxel() { return ( VoxelAttribute*)data(); }
@@ -80,6 +81,7 @@ public:
void add(const float& f);
void add(const float3& f);
+ void add(const uchar4& f);
void add(const Transform& f);
void add(const VoxelAttribute& f);
void add(const char *data);
diff --git a/intern/cycles/render/curves.cpp b/intern/cycles/render/curves.cpp
index 2c96ffa655e..dc7665fe144 100644
--- a/intern/cycles/render/curves.cpp
+++ b/intern/cycles/render/curves.cpp
@@ -46,8 +46,9 @@ void curvebounds(float *lower, float *upper, float3 *p, int dim)
float discroot = curve_coef[2] * curve_coef[2] - 3 * curve_coef[3] * curve_coef[1];
float ta = -1.0f;
float tb = -1.0f;
+
if(discroot >= 0) {
- discroot = sqrt(discroot);
+ discroot = sqrtf(discroot);
ta = (-curve_coef[2] - discroot) / (3 * curve_coef[3]);
tb = (-curve_coef[2] + discroot) / (3 * curve_coef[3]);
ta = (ta > 1.0f || ta < 0.0f) ? -1.0f : ta;
@@ -56,20 +57,21 @@ void curvebounds(float *lower, float *upper, float3 *p, int dim)
*upper = max(p1[dim],p2[dim]);
*lower = min(p1[dim],p2[dim]);
+
float exa = p1[dim];
float exb = p2[dim];
- float t2;
- float t3;
+
if(ta >= 0.0f) {
- t2 = ta * ta;
- t3 = t2 * ta;
+ float t2 = ta * ta;
+ float t3 = t2 * ta;
exa = curve_coef[3] * t3 + curve_coef[2] * t2 + curve_coef[1] * ta + curve_coef[0];
}
if(tb >= 0.0f) {
- t2 = tb * tb;
- t3 = t2 * tb;
+ float t2 = tb * tb;
+ float t3 = t2 * tb;
exb = curve_coef[3] * t3 + curve_coef[2] * t2 + curve_coef[1] * tb + curve_coef[0];
}
+
*upper = max(*upper, max(exa,exb));
*lower = min(*lower, min(exa,exb));
}
diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp
index 77cd6d5e79d..8369df5e137 100644
--- a/intern/cycles/render/image.cpp
+++ b/intern/cycles/render/image.cpp
@@ -157,7 +157,8 @@ static bool image_equals(ImageManager::Image *image, const string& filename, voi
image->interpolation == interpolation;
}
-int ImageManager::add_image(const string& filename, void *builtin_data, bool animated, bool& is_float, bool& is_linear, InterpolationType interpolation, bool use_alpha)
+int ImageManager::add_image(const string& filename, void *builtin_data, bool animated, float frame,
+ bool& is_float, bool& is_linear, InterpolationType interpolation, bool use_alpha)
{
Image *img;
size_t slot;
@@ -168,8 +169,13 @@ int ImageManager::add_image(const string& filename, void *builtin_data, bool ani
if(is_float) {
/* find existing image */
for(slot = 0; slot < float_images.size(); slot++) {
- if(float_images[slot] && image_equals(float_images[slot], filename, builtin_data, interpolation)) {
- float_images[slot]->users++;
+ img = float_images[slot];
+ if(img && image_equals(img, filename, builtin_data, interpolation)) {
+ if(img->frame != frame) {
+ img->frame = frame;
+ img->need_load = true;
+ }
+ img->users++;
return slot;
}
}
@@ -197,6 +203,7 @@ int ImageManager::add_image(const string& filename, void *builtin_data, bool ani
img->builtin_data = builtin_data;
img->need_load = true;
img->animated = animated;
+ img->frame = frame;
img->interpolation = interpolation;
img->users = 1;
img->use_alpha = use_alpha;
@@ -205,8 +212,13 @@ int ImageManager::add_image(const string& filename, void *builtin_data, bool ani
}
else {
for(slot = 0; slot < images.size(); slot++) {
- if(images[slot] && image_equals(images[slot], filename, builtin_data, interpolation)) {
- images[slot]->users++;
+ img = images[slot];
+ if(img && image_equals(img, filename, builtin_data, interpolation)) {
+ if(img->frame != frame) {
+ img->frame = frame;
+ img->need_load = true;
+ }
+ img->users++;
return slot+tex_image_byte_start;
}
}
@@ -234,6 +246,7 @@ int ImageManager::add_image(const string& filename, void *builtin_data, bool ani
img->builtin_data = builtin_data;
img->need_load = true;
img->animated = animated;
+ img->frame = frame;
img->interpolation = interpolation;
img->users = 1;
img->use_alpha = use_alpha;
@@ -242,6 +255,7 @@ int ImageManager::add_image(const string& filename, void *builtin_data, bool ani
slot += tex_image_byte_start;
}
+
need_update = true;
return slot;
@@ -351,6 +365,7 @@ bool ImageManager::file_load_image(Image *img, device_vector<uchar4>& tex_img)
/* read RGBA pixels */
uchar *pixels = (uchar*)tex_img.resize(width, height, depth);
+ bool cmyk = false;
if(in) {
if(depth <= 1) {
@@ -366,6 +381,8 @@ bool ImageManager::file_load_image(Image *img, device_vector<uchar4>& tex_img)
in->read_image(TypeDesc::UINT8, (uchar*)pixels);
}
+ cmyk = strcmp(in->format_name(), "jpeg") == 0 && components == 4;
+
in->close();
delete in;
}
@@ -373,7 +390,17 @@ bool ImageManager::file_load_image(Image *img, device_vector<uchar4>& tex_img)
builtin_image_pixels_cb(img->filename, img->builtin_data, pixels);
}
- if(components == 2) {
+ if(cmyk) {
+ /* CMYK */
+ for(int i = width*height*depth-1; i >= 0; i--) {
+ pixels[i*4+2] = (pixels[i*4+2]*pixels[i*4+3])/255;
+ pixels[i*4+1] = (pixels[i*4+1]*pixels[i*4+3])/255;
+ pixels[i*4+0] = (pixels[i*4+0]*pixels[i*4+3])/255;
+ pixels[i*4+3] = 255;
+ }
+ }
+ else if(components == 2) {
+ /* grayscale + alpha */
for(int i = width*height*depth-1; i >= 0; i--) {
pixels[i*4+3] = pixels[i*2+1];
pixels[i*4+2] = pixels[i*2+0];
@@ -382,6 +409,7 @@ bool ImageManager::file_load_image(Image *img, device_vector<uchar4>& tex_img)
}
}
else if(components == 3) {
+ /* RGB */
for(int i = width*height*depth-1; i >= 0; i--) {
pixels[i*4+3] = 255;
pixels[i*4+2] = pixels[i*3+2];
@@ -390,6 +418,7 @@ bool ImageManager::file_load_image(Image *img, device_vector<uchar4>& tex_img)
}
}
else if(components == 1) {
+ /* grayscale */
for(int i = width*height*depth-1; i >= 0; i--) {
pixels[i*4+3] = 255;
pixels[i*4+2] = pixels[i];
@@ -458,6 +487,7 @@ bool ImageManager::file_load_float_image(Image *img, device_vector<float4>& tex_
/* read RGBA pixels */
float *pixels = (float*)tex_img.resize(width, height, depth);
+ bool cmyk = false;
if(in) {
float *readpixels = pixels;
@@ -492,6 +522,8 @@ bool ImageManager::file_load_float_image(Image *img, device_vector<float4>& tex_
tmppixels.clear();
}
+ cmyk = strcmp(in->format_name(), "jpeg") == 0 && components == 4;
+
in->close();
delete in;
}
@@ -499,7 +531,17 @@ bool ImageManager::file_load_float_image(Image *img, device_vector<float4>& tex_
builtin_image_float_pixels_cb(img->filename, img->builtin_data, pixels);
}
- if(components == 2) {
+ if(cmyk) {
+ /* CMYK */
+ for(int i = width*height*depth-1; i >= 0; i--) {
+ pixels[i*4+3] = 255;
+ pixels[i*4+2] = (pixels[i*4+2]*pixels[i*4+3])/255;
+ pixels[i*4+1] = (pixels[i*4+1]*pixels[i*4+3])/255;
+ pixels[i*4+0] = (pixels[i*4+0]*pixels[i*4+3])/255;
+ }
+ }
+ else if(components == 2) {
+ /* grayscale + alpha */
for(int i = width*height*depth-1; i >= 0; i--) {
pixels[i*4+3] = pixels[i*2+1];
pixels[i*4+2] = pixels[i*2+0];
@@ -508,6 +550,7 @@ bool ImageManager::file_load_float_image(Image *img, device_vector<float4>& tex_
}
}
else if(components == 3) {
+ /* RGB */
for(int i = width*height*depth-1; i >= 0; i--) {
pixels[i*4+3] = 1.0f;
pixels[i*4+2] = pixels[i*3+2];
@@ -516,6 +559,7 @@ bool ImageManager::file_load_float_image(Image *img, device_vector<float4>& tex_
}
}
else if(components == 1) {
+ /* grayscale */
for(int i = width*height*depth-1; i >= 0; i--) {
pixels[i*4+3] = 1.0f;
pixels[i*4+2] = pixels[i];
@@ -576,7 +620,8 @@ void ImageManager::device_load_image(Device *device, DeviceScene *dscene, int sl
string name;
- if(slot >= 10) name = string_printf("__tex_image_float_0%d", slot);
+ if(slot >= 100) name = string_printf("__tex_image_float_%d", slot);
+ else if(slot >= 10) name = string_printf("__tex_image_float_0%d", slot);
else name = string_printf("__tex_image_float_00%d", slot);
if(!pack_images) {
@@ -607,7 +652,8 @@ void ImageManager::device_load_image(Device *device, DeviceScene *dscene, int sl
string name;
- if(slot >= 10) name = string_printf("__tex_image_0%d", slot);
+ if(slot >= 100) name = string_printf("__tex_image_%d", slot);
+ else if(slot >= 10) name = string_printf("__tex_image_0%d", slot);
else name = string_printf("__tex_image_00%d", slot);
if(!pack_images) {
diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h
index 8abf2a33d74..50ea346c034 100644
--- a/intern/cycles/render/image.h
+++ b/intern/cycles/render/image.h
@@ -29,7 +29,7 @@
CCL_NAMESPACE_BEGIN
/* generic */
-#define TEX_NUM_IMAGES 95
+#define TEX_NUM_IMAGES 94
#define TEX_IMAGE_BYTE_START TEX_NUM_FLOAT_IMAGES
/* extended gpu */
@@ -55,7 +55,8 @@ public:
ImageManager();
~ImageManager();
- int add_image(const string& filename, void *builtin_data, bool animated, bool& is_float, bool& is_linear, InterpolationType interpolation, bool use_alpha);
+ int add_image(const string& filename, void *builtin_data, bool animated, float frame,
+ bool& is_float, bool& is_linear, InterpolationType interpolation, bool use_alpha);
void remove_image(int slot);
void remove_image(const string& filename, void *builtin_data, InterpolationType interpolation);
bool is_float_image(const string& filename, void *builtin_data, bool& is_linear);
@@ -82,6 +83,7 @@ public:
bool use_alpha;
bool need_load;
bool animated;
+ float frame;
InterpolationType interpolation;
int users;
diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp
index 051ba1baf3a..4a8b490b1ad 100644
--- a/intern/cycles/render/integrator.cpp
+++ b/intern/cycles/render/integrator.cpp
@@ -101,7 +101,6 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
if(!transparent_shadows)
kintegrator->transparent_shadows = false;
- kintegrator->volume_homogeneous_sampling = volume_homogeneous_sampling;
kintegrator->volume_max_steps = volume_max_steps;
kintegrator->volume_step_size = volume_step_size;
@@ -125,8 +124,15 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
kintegrator->mesh_light_samples = mesh_light_samples;
kintegrator->subsurface_samples = subsurface_samples;
kintegrator->volume_samples = volume_samples;
- kintegrator->sample_all_lights_direct = sample_all_lights_direct;
- kintegrator->sample_all_lights_indirect = sample_all_lights_indirect;
+
+ if(method == BRANCHED_PATH) {
+ kintegrator->sample_all_lights_direct = sample_all_lights_direct;
+ kintegrator->sample_all_lights_indirect = sample_all_lights_indirect;
+ }
+ else {
+ kintegrator->sample_all_lights_direct = false;
+ kintegrator->sample_all_lights_indirect = false;
+ }
kintegrator->sampling_pattern = sampling_pattern;
kintegrator->aa_samples = aa_samples;
diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp
index 9c5ddd55010..3a95f5c106b 100644
--- a/intern/cycles/render/mesh.cpp
+++ b/intern/cycles/render/mesh.cpp
@@ -377,12 +377,10 @@ void Mesh::add_vertex_normals()
}
}
-void Mesh::pack_normals(Scene *scene, float4 *normal, float4 *vnormal)
+void Mesh::pack_normals(Scene *scene, float *tri_shader, float4 *vnormal)
{
- Attribute *attr_fN = attributes.find(ATTR_STD_FACE_NORMAL);
Attribute *attr_vN = attributes.find(ATTR_STD_VERTEX_NORMAL);
- float3 *fN = attr_fN->data_float3();
float3 *vN = attr_vN->data_float3();
int shader_id = 0;
uint last_shader = -1;
@@ -394,24 +392,15 @@ void Mesh::pack_normals(Scene *scene, float4 *normal, float4 *vnormal)
bool do_transform = transform_applied;
Transform ntfm = transform_normal;
+ /* save shader */
for(size_t i = 0; i < triangles_size; i++) {
- float3 fNi = fN[i];
-
- if(do_transform)
- fNi = normalize(transform_direction(&ntfm, fNi));
-
- normal[i].x = fNi.x;
- normal[i].y = fNi.y;
- normal[i].z = fNi.z;
-
- /* stuff shader id in here too */
if(shader_ptr[i] != last_shader || last_smooth != smooth[i]) {
last_shader = shader_ptr[i];
last_smooth = smooth[i];
shader_id = scene->shader_manager->get_shader_id(last_shader, this, last_smooth);
}
- normal[i].w = __int_as_float(shader_id);
+ tri_shader[i] = __int_as_float(shader_id);
}
size_t verts_size = verts.size();
@@ -756,7 +745,7 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce
device->tex_alloc("__attributes_map", dscene->attributes_map);
}
-static void update_attribute_element_offset(Mesh *mesh, vector<float>& attr_float, vector<float4>& attr_float3,
+static void update_attribute_element_offset(Mesh *mesh, vector<float>& attr_float, vector<float4>& attr_float3, vector<uchar4>& attr_uchar4,
Attribute *mattr, TypeDesc& type, int& offset, AttributeElement& element)
{
if(mattr) {
@@ -777,6 +766,15 @@ static void update_attribute_element_offset(Mesh *mesh, vector<float>& attr_floa
VoxelAttribute *voxel_data = mattr->data_voxel();
offset = voxel_data->slot;
}
+ if(mattr->element == ATTR_ELEMENT_CORNER_BYTE) {
+ uchar4 *data = mattr->data_uchar4();
+ offset = attr_uchar4.size();
+
+ attr_uchar4.resize(attr_uchar4.size() + size);
+
+ for(size_t k = 0; k < size; k++)
+ attr_uchar4[offset+k] = data[k];
+ }
else if(mattr->type == TypeDesc::TypeFloat) {
float *data = mattr->data_float();
offset = attr_float.size();
@@ -813,7 +811,7 @@ static void update_attribute_element_offset(Mesh *mesh, vector<float>& attr_floa
offset -= mesh->vert_offset;
else if(element == ATTR_ELEMENT_FACE)
offset -= mesh->tri_offset;
- else if(element == ATTR_ELEMENT_CORNER)
+ else if(element == ATTR_ELEMENT_CORNER || element == ATTR_ELEMENT_CORNER_BYTE)
offset -= 3*mesh->tri_offset;
else if(element == ATTR_ELEMENT_CURVE)
offset -= mesh->curve_offset;
@@ -854,6 +852,7 @@ void MeshManager::device_update_attributes(Device *device, DeviceScene *dscene,
* maps next */
vector<float> attr_float;
vector<float4> attr_float3;
+ vector<uchar4> attr_uchar4;
for(size_t i = 0; i < scene->meshes.size(); i++) {
Mesh *mesh = scene->meshes[i];
@@ -874,10 +873,10 @@ void MeshManager::device_update_attributes(Device *device, DeviceScene *dscene,
memcpy(triangle_mattr->data_float3(), &mesh->verts[0], sizeof(float3)*mesh->verts.size());
}
- update_attribute_element_offset(mesh, attr_float, attr_float3, triangle_mattr,
+ update_attribute_element_offset(mesh, attr_float, attr_float3, attr_uchar4, triangle_mattr,
req.triangle_type, req.triangle_offset, req.triangle_element);
- update_attribute_element_offset(mesh, attr_float, attr_float3, curve_mattr,
+ update_attribute_element_offset(mesh, attr_float, attr_float3, attr_uchar4, curve_mattr,
req.curve_type, req.curve_offset, req.curve_element);
if(progress.get_cancel()) return;
@@ -903,6 +902,10 @@ void MeshManager::device_update_attributes(Device *device, DeviceScene *dscene,
dscene->attributes_float3.copy(&attr_float3[0], attr_float3.size());
device->tex_alloc("__attributes_float3", dscene->attributes_float3);
}
+ if(attr_uchar4.size()) {
+ dscene->attributes_uchar4.copy(&attr_uchar4[0], attr_uchar4.size());
+ device->tex_alloc("__attributes_uchar4", dscene->attributes_uchar4);
+ }
}
void MeshManager::device_update_mesh(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress)
@@ -932,13 +935,13 @@ void MeshManager::device_update_mesh(Device *device, DeviceScene *dscene, Scene
/* normals */
progress.set_status("Updating Mesh", "Computing normals");
- float4 *normal = dscene->tri_normal.resize(tri_size);
+ float *tri_shader = dscene->tri_shader.resize(tri_size);
float4 *vnormal = dscene->tri_vnormal.resize(vert_size);
float4 *tri_verts = dscene->tri_verts.resize(vert_size);
float4 *tri_vindex = dscene->tri_vindex.resize(tri_size);
foreach(Mesh *mesh, scene->meshes) {
- mesh->pack_normals(scene, &normal[mesh->tri_offset], &vnormal[mesh->vert_offset]);
+ mesh->pack_normals(scene, &tri_shader[mesh->tri_offset], &vnormal[mesh->vert_offset]);
mesh->pack_verts(&tri_verts[mesh->vert_offset], &tri_vindex[mesh->tri_offset], mesh->vert_offset);
if(progress.get_cancel()) return;
@@ -947,7 +950,7 @@ void MeshManager::device_update_mesh(Device *device, DeviceScene *dscene, Scene
/* vertex coordinates */
progress.set_status("Updating Mesh", "Copying Mesh to device");
- device->tex_alloc("__tri_normal", dscene->tri_normal);
+ device->tex_alloc("__tri_shader", dscene->tri_shader);
device->tex_alloc("__tri_vnormal", dscene->tri_vnormal);
device->tex_alloc("__tri_verts", dscene->tri_verts);
device->tex_alloc("__tri_vindex", dscene->tri_vindex);
@@ -1119,7 +1122,7 @@ void MeshManager::device_free(Device *device, DeviceScene *dscene)
device->tex_free(dscene->prim_visibility);
device->tex_free(dscene->prim_index);
device->tex_free(dscene->prim_object);
- device->tex_free(dscene->tri_normal);
+ device->tex_free(dscene->tri_shader);
device->tex_free(dscene->tri_vnormal);
device->tex_free(dscene->tri_vindex);
device->tex_free(dscene->tri_verts);
@@ -1128,6 +1131,7 @@ void MeshManager::device_free(Device *device, DeviceScene *dscene)
device->tex_free(dscene->attributes_map);
device->tex_free(dscene->attributes_float);
device->tex_free(dscene->attributes_float3);
+ device->tex_free(dscene->attributes_uchar4);
dscene->bvh_nodes.clear();
dscene->object_node.clear();
@@ -1136,7 +1140,7 @@ void MeshManager::device_free(Device *device, DeviceScene *dscene)
dscene->prim_visibility.clear();
dscene->prim_index.clear();
dscene->prim_object.clear();
- dscene->tri_normal.clear();
+ dscene->tri_shader.clear();
dscene->tri_vnormal.clear();
dscene->tri_vindex.clear();
dscene->tri_verts.clear();
@@ -1145,6 +1149,7 @@ void MeshManager::device_free(Device *device, DeviceScene *dscene)
dscene->attributes_map.clear();
dscene->attributes_float.clear();
dscene->attributes_float3.clear();
+ dscene->attributes_uchar4.clear();
#ifdef WITH_OSL
OSLGlobals *og = (OSLGlobals*)device->osl_memory();
diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h
index 247e3dd555e..5ee774bacc1 100644
--- a/intern/cycles/render/mesh.h
+++ b/intern/cycles/render/mesh.h
@@ -120,7 +120,7 @@ public:
void add_face_normals();
void add_vertex_normals();
- void pack_normals(Scene *scene, float4 *normal, float4 *vnormal);
+ void pack_normals(Scene *scene, float *shader, float4 *vnormal);
void pack_verts(float4 *tri_verts, float4 *tri_vindex, size_t vert_offset);
void pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, size_t curvekey_offset);
void compute_bvh(SceneParams *params, Progress *progress, int n, int total);
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp
index 1a1667ff1b3..251afe9a0a2 100644
--- a/intern/cycles/render/nodes.cpp
+++ b/intern/cycles/render/nodes.cpp
@@ -243,7 +243,9 @@ void ImageTextureNode::compile(SVMCompiler& compiler)
image_manager = compiler.image_manager;
if(is_float == -1) {
bool is_float_bool;
- slot = image_manager->add_image(filename, builtin_data, animated, is_float_bool, is_linear, interpolation, use_alpha);
+ slot = image_manager->add_image(filename, builtin_data,
+ animated, 0, is_float_bool, is_linear,
+ interpolation, use_alpha);
is_float = (int)is_float_bool;
}
@@ -305,10 +307,32 @@ void ImageTextureNode::compile(OSLCompiler& compiler)
tex_mapping.compile(compiler);
- if(is_float == -1)
- is_float = (int)image_manager->is_float_image(filename, NULL, is_linear);
+ image_manager = compiler.image_manager;
+ if(is_float == -1) {
+ if(builtin_data == NULL) {
+ is_float = (int)image_manager->is_float_image(filename, NULL, is_linear);
+ }
+ else {
+ bool is_float_bool;
+ slot = image_manager->add_image(filename, builtin_data,
+ animated, 0, is_float_bool, is_linear,
+ interpolation, use_alpha);
+ is_float = (int)is_float_bool;
+ }
+ }
- compiler.parameter("filename", filename.c_str());
+ if(slot == -1) {
+ compiler.parameter("filename", filename.c_str());
+ }
+ else {
+ /* TODO(sergey): It's not so simple to pass custom attribute
+ * to the texture() function in order to make builtin images
+ * support more clear. So we use special file name which is
+ * "@<slot_number>" and check whether file name matches this
+ * mask in the OSLRenderServices::texture().
+ */
+ compiler.parameter("filename", string_printf("@%d", slot).c_str());
+ }
if(is_linear || color_space != "Color")
compiler.parameter("color_space", "Linear");
else
@@ -408,7 +432,9 @@ void EnvironmentTextureNode::compile(SVMCompiler& compiler)
image_manager = compiler.image_manager;
if(slot == -1) {
bool is_float_bool;
- slot = image_manager->add_image(filename, builtin_data, animated, is_float_bool, is_linear, INTERPOLATION_LINEAR, use_alpha);
+ slot = image_manager->add_image(filename, builtin_data,
+ animated, 0, is_float_bool, is_linear,
+ INTERPOLATION_LINEAR, use_alpha);
is_float = (int)is_float_bool;
}
@@ -459,10 +485,28 @@ void EnvironmentTextureNode::compile(OSLCompiler& compiler)
tex_mapping.compile(compiler);
- if(is_float == -1)
- is_float = (int)image_manager->is_float_image(filename, NULL, is_linear);
+ /* See comments in ImageTextureNode::compile about support
+ * of builtin images.
+ */
+ if(is_float == -1) {
+ if(builtin_data == NULL) {
+ is_float = (int)image_manager->is_float_image(filename, NULL, is_linear);
+ }
+ else {
+ bool is_float_bool;
+ slot = image_manager->add_image(filename, builtin_data,
+ animated, 0, is_float_bool, is_linear,
+ INTERPOLATION_LINEAR, use_alpha);
+ is_float = (int)is_float_bool;
+ }
+ }
- compiler.parameter("filename", filename.c_str());
+ if(slot == -1) {
+ compiler.parameter("filename", filename.c_str());
+ }
+ else {
+ compiler.parameter("filename", string_printf("@%d", slot).c_str());
+ }
compiler.parameter("projection", projection);
if(is_linear || color_space != "Color")
compiler.parameter("color_space", "Linear");
@@ -1543,11 +1587,24 @@ void BsdfNode::compile(OSLCompiler& compiler)
assert(0);
}
-/* Ward BSDF Closure */
+/* Anisotropic BSDF Closure */
+
+static ShaderEnum aniso_distribution_init()
+{
+ ShaderEnum enm;
+
+ enm.insert("Beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID);
+ enm.insert("GGX", CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID);
+ enm.insert("Ashikhmin-Shirley", CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID);
+
+ return enm;
+}
+
+ShaderEnum AnisotropicBsdfNode::distribution_enum = aniso_distribution_init();
-WardBsdfNode::WardBsdfNode()
+AnisotropicBsdfNode::AnisotropicBsdfNode()
{
- closure = CLOSURE_BSDF_WARD_ID;
+ distribution = ustring("GGX");
add_input("Tangent", SHADER_SOCKET_VECTOR, ShaderInput::TANGENT);
@@ -1556,7 +1613,7 @@ WardBsdfNode::WardBsdfNode()
add_input("Rotation", SHADER_SOCKET_FLOAT, 0.0f);
}
-void WardBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes)
+void AnisotropicBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes)
{
if(shader->has_surface) {
ShaderInput *tangent_in = input("Tangent");
@@ -1568,14 +1625,17 @@ void WardBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes)
ShaderNode::attributes(shader, attributes);
}
-void WardBsdfNode::compile(SVMCompiler& compiler)
+void AnisotropicBsdfNode::compile(SVMCompiler& compiler)
{
+ closure = (ClosureType)distribution_enum[distribution];
+
BsdfNode::compile(compiler, input("Roughness"), input("Anisotropy"), input("Rotation"));
}
-void WardBsdfNode::compile(OSLCompiler& compiler)
+void AnisotropicBsdfNode::compile(OSLCompiler& compiler)
{
- compiler.add(this, "node_ward_bsdf");
+ compiler.parameter("distribution", distribution);
+ compiler.add(this, "node_anisotropic_bsdf");
}
/* Glossy BSDF Closure */
@@ -1587,6 +1647,7 @@ static ShaderEnum glossy_distribution_init()
enm.insert("Sharp", CLOSURE_BSDF_REFLECTION_ID);
enm.insert("Beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_ID);
enm.insert("GGX", CLOSURE_BSDF_MICROFACET_GGX_ID);
+ enm.insert("Ashikhmin-Shirley", CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID);
return enm;
}
@@ -1595,7 +1656,7 @@ ShaderEnum GlossyBsdfNode::distribution_enum = glossy_distribution_init();
GlossyBsdfNode::GlossyBsdfNode()
{
- distribution = ustring("Beckmann");
+ distribution = ustring("GGX");
add_input("Roughness", SHADER_SOCKET_FLOAT, 0.2f);
}
@@ -3002,13 +3063,13 @@ void CombineRGBNode::compile(SVMCompiler& compiler)
compiler.stack_assign(color_out);
compiler.stack_assign(red_in);
- compiler.add_node(NODE_COMBINE_RGB, red_in->stack_offset, 0, color_out->stack_offset);
+ compiler.add_node(NODE_COMBINE_VECTOR, red_in->stack_offset, 0, color_out->stack_offset);
compiler.stack_assign(green_in);
- compiler.add_node(NODE_COMBINE_RGB, green_in->stack_offset, 1, color_out->stack_offset);
+ compiler.add_node(NODE_COMBINE_VECTOR, green_in->stack_offset, 1, color_out->stack_offset);
compiler.stack_assign(blue_in);
- compiler.add_node(NODE_COMBINE_RGB, blue_in->stack_offset, 2, color_out->stack_offset);
+ compiler.add_node(NODE_COMBINE_VECTOR, blue_in->stack_offset, 2, color_out->stack_offset);
}
void CombineRGBNode::compile(OSLCompiler& compiler)
@@ -3016,6 +3077,40 @@ void CombineRGBNode::compile(OSLCompiler& compiler)
compiler.add(this, "node_combine_rgb");
}
+/* Combine XYZ */
+CombineXYZNode::CombineXYZNode()
+: ShaderNode("combine_xyz")
+{
+ add_input("X", SHADER_SOCKET_FLOAT);
+ add_input("Y", SHADER_SOCKET_FLOAT);
+ add_input("Z", SHADER_SOCKET_FLOAT);
+ add_output("Vector", SHADER_SOCKET_VECTOR);
+}
+
+void CombineXYZNode::compile(SVMCompiler& compiler)
+{
+ ShaderInput *x_in = input("X");
+ ShaderInput *y_in = input("Y");
+ ShaderInput *z_in = input("Z");
+ ShaderOutput *vector_out = output("Vector");
+
+ compiler.stack_assign(vector_out);
+
+ compiler.stack_assign(x_in);
+ compiler.add_node(NODE_COMBINE_VECTOR, x_in->stack_offset, 0, vector_out->stack_offset);
+
+ compiler.stack_assign(y_in);
+ compiler.add_node(NODE_COMBINE_VECTOR, y_in->stack_offset, 1, vector_out->stack_offset);
+
+ compiler.stack_assign(z_in);
+ compiler.add_node(NODE_COMBINE_VECTOR, z_in->stack_offset, 2, vector_out->stack_offset);
+}
+
+void CombineXYZNode::compile(OSLCompiler& compiler)
+{
+ compiler.add(this, "node_combine_xyz");
+}
+
/* Combine HSV */
CombineHSVNode::CombineHSVNode()
: ShaderNode("combine_hsv")
@@ -3126,13 +3221,13 @@ void SeparateRGBNode::compile(SVMCompiler& compiler)
compiler.stack_assign(color_in);
compiler.stack_assign(red_out);
- compiler.add_node(NODE_SEPARATE_RGB, color_in->stack_offset, 0, red_out->stack_offset);
+ compiler.add_node(NODE_SEPARATE_VECTOR, color_in->stack_offset, 0, red_out->stack_offset);
compiler.stack_assign(green_out);
- compiler.add_node(NODE_SEPARATE_RGB, color_in->stack_offset, 1, green_out->stack_offset);
+ compiler.add_node(NODE_SEPARATE_VECTOR, color_in->stack_offset, 1, green_out->stack_offset);
compiler.stack_assign(blue_out);
- compiler.add_node(NODE_SEPARATE_RGB, color_in->stack_offset, 2, blue_out->stack_offset);
+ compiler.add_node(NODE_SEPARATE_VECTOR, color_in->stack_offset, 2, blue_out->stack_offset);
}
void SeparateRGBNode::compile(OSLCompiler& compiler)
@@ -3140,6 +3235,40 @@ void SeparateRGBNode::compile(OSLCompiler& compiler)
compiler.add(this, "node_separate_rgb");
}
+/* Separate XYZ */
+SeparateXYZNode::SeparateXYZNode()
+: ShaderNode("separate_xyz")
+{
+ add_input("Vector", SHADER_SOCKET_VECTOR);
+ add_output("X", SHADER_SOCKET_FLOAT);
+ add_output("Y", SHADER_SOCKET_FLOAT);
+ add_output("Z", SHADER_SOCKET_FLOAT);
+}
+
+void SeparateXYZNode::compile(SVMCompiler& compiler)
+{
+ ShaderInput *vector_in = input("Vector");
+ ShaderOutput *x_out = output("X");
+ ShaderOutput *y_out = output("Y");
+ ShaderOutput *z_out = output("Z");
+
+ compiler.stack_assign(vector_in);
+
+ compiler.stack_assign(x_out);
+ compiler.add_node(NODE_SEPARATE_VECTOR, vector_in->stack_offset, 0, x_out->stack_offset);
+
+ compiler.stack_assign(y_out);
+ compiler.add_node(NODE_SEPARATE_VECTOR, vector_in->stack_offset, 1, y_out->stack_offset);
+
+ compiler.stack_assign(z_out);
+ compiler.add_node(NODE_SEPARATE_VECTOR, vector_in->stack_offset, 2, z_out->stack_offset);
+}
+
+void SeparateXYZNode::compile(OSLCompiler& compiler)
+{
+ compiler.add(this, "node_separate_xyz");
+}
+
/* Separate HSV */
SeparateHSVNode::SeparateHSVNode()
: ShaderNode("separate_hsv")
@@ -4121,4 +4250,3 @@ void TangentNode::compile(OSLCompiler& compiler)
}
CCL_NAMESPACE_END
-
diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h
index 8bdce9901b2..31b6f4e50c4 100644
--- a/intern/cycles/render/nodes.h
+++ b/intern/cycles/render/nodes.h
@@ -218,9 +218,13 @@ public:
bool scattering;
};
-class WardBsdfNode : public BsdfNode {
+class AnisotropicBsdfNode : public BsdfNode {
public:
- SHADER_NODE_CLASS(WardBsdfNode)
+ SHADER_NODE_CLASS(AnisotropicBsdfNode)
+
+ ustring distribution;
+ static ShaderEnum distribution_enum;
+
void attributes(Shader *shader, AttributeRequestSet *attributes);
};
@@ -451,6 +455,11 @@ public:
SHADER_NODE_CLASS(CombineHSVNode)
};
+class CombineXYZNode : public ShaderNode {
+public:
+ SHADER_NODE_CLASS(CombineXYZNode)
+};
+
class GammaNode : public ShaderNode {
public:
SHADER_NODE_CLASS(GammaNode)
@@ -471,6 +480,11 @@ public:
SHADER_NODE_CLASS(SeparateHSVNode)
};
+class SeparateXYZNode : public ShaderNode {
+public:
+ SHADER_NODE_CLASS(SeparateXYZNode)
+};
+
class HSVNode : public ShaderNode {
public:
SHADER_NODE_CLASS(HSVNode)
diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h
index d657e48e478..e5c7444c92d 100644
--- a/intern/cycles/render/scene.h
+++ b/intern/cycles/render/scene.h
@@ -69,7 +69,7 @@ public:
device_vector<uint> prim_object;
/* mesh */
- device_vector<float4> tri_normal;
+ device_vector<float> tri_shader;
device_vector<float4> tri_vnormal;
device_vector<float4> tri_vindex;
device_vector<float4> tri_verts;
@@ -85,6 +85,7 @@ public:
device_vector<uint4> attributes_map;
device_vector<float> attributes_float;
device_vector<float4> attributes_float3;
+ device_vector<uchar4> attributes_uchar4;
/* lights */
device_vector<float4> light_distribution;
diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp
index 0f9a5a5d39d..b9685807e16 100644
--- a/intern/cycles/render/shader.cpp
+++ b/intern/cycles/render/shader.cpp
@@ -31,6 +31,95 @@
CCL_NAMESPACE_BEGIN
+/* Beckmann sampling precomputed table, see bsdf_microfacet.h */
+
+/* 2D slope distribution (alpha = 1.0) */
+static float beckmann_table_P22(const float slope_x, const float slope_y)
+{
+ return expf(-(slope_x*slope_x + slope_y*slope_y));
+}
+
+/* maximal slope amplitude (range that contains 99.99% of the distribution) */
+static float beckmann_table_slope_max()
+{
+ return 6.0;
+}
+
+static void beckmann_table_rows(float *table, int row_from, int row_to)
+{
+ /* allocate temporary data */
+ const int DATA_TMP_SIZE = 512;
+ vector<double> slope_x(DATA_TMP_SIZE);
+ vector<double> CDF_P22_omega_i(DATA_TMP_SIZE);
+
+ /* loop over incident directions */
+ for(int index_theta = row_from; index_theta < row_to; index_theta++) {
+ /* incident vector */
+ const float cos_theta = index_theta / (BECKMANN_TABLE_SIZE - 1.0f);
+ const float sin_theta = safe_sqrtf(1.0f - cos_theta*cos_theta);
+
+ /* for a given incident vector
+ * integrate P22_{omega_i}(x_slope, 1, 1), Eq. (10) */
+ slope_x[0] = -beckmann_table_slope_max();
+ CDF_P22_omega_i[0] = 0;
+
+ for(int index_slope_x = 1; index_slope_x < DATA_TMP_SIZE; ++index_slope_x) {
+ /* slope_x */
+ slope_x[index_slope_x] = -beckmann_table_slope_max() + 2.0f * beckmann_table_slope_max() * index_slope_x/(DATA_TMP_SIZE - 1.0f);
+
+ /* dot product with incident vector */
+ float dot_product = fmaxf(0.0f, -(float)slope_x[index_slope_x]*sin_theta + cos_theta);
+ /* marginalize P22_{omega_i}(x_slope, 1, 1), Eq. (10) */
+ float P22_omega_i = 0.0f;
+
+ for(int j = 0; j < 100; ++j) {
+ float slope_y = -beckmann_table_slope_max() + 2.0f * beckmann_table_slope_max() * j * (1.0f/99.0f);
+ P22_omega_i += dot_product * beckmann_table_P22((float)slope_x[index_slope_x], slope_y);
+ }
+
+ /* CDF of P22_{omega_i}(x_slope, 1, 1), Eq. (10) */
+ CDF_P22_omega_i[index_slope_x] = CDF_P22_omega_i[index_slope_x - 1] + P22_omega_i;
+ }
+
+ /* renormalize CDF_P22_omega_i */
+ for(int index_slope_x = 1; index_slope_x < DATA_TMP_SIZE; ++index_slope_x)
+ CDF_P22_omega_i[index_slope_x] /= CDF_P22_omega_i[DATA_TMP_SIZE - 1];
+
+ /* loop over random number U1 */
+ int index_slope_x = 0;
+
+ for(int index_U = 0; index_U < BECKMANN_TABLE_SIZE; ++index_U) {
+ const float U = 0.0000001f + 0.9999998f * index_U / (float)(BECKMANN_TABLE_SIZE - 1);
+
+ /* inverse CDF_P22_omega_i, solve Eq.(11) */
+ while(CDF_P22_omega_i[index_slope_x] <= U)
+ ++index_slope_x;
+
+ const double interp =
+ (CDF_P22_omega_i[index_slope_x] - U) /
+ (CDF_P22_omega_i[index_slope_x] - CDF_P22_omega_i[index_slope_x - 1]);
+
+ /* store value */
+ table[index_U + index_theta*BECKMANN_TABLE_SIZE] = (float)(
+ interp * slope_x[index_slope_x - 1]
+ + (1.0f-interp) * slope_x[index_slope_x]);
+ }
+ }
+}
+
+static void beckmann_table_build(vector<float>& table)
+{
+ table.resize(BECKMANN_TABLE_SIZE*BECKMANN_TABLE_SIZE);
+
+ /* multithreaded build */
+ TaskPool pool;
+
+ for(int i = 0; i < BECKMANN_TABLE_SIZE; i+=8)
+ pool.push(function_bind(&beckmann_table_rows, &table[0], i, i+8));
+
+ pool.wait_work();
+}
+
/* Shader */
Shader::Shader()
@@ -44,6 +133,7 @@ Shader::Shader()
use_mis = true;
use_transparent_shadow = true;
heterogeneous_volume = true;
+ volume_sampling_method = 0;
has_surface = false;
has_surface_transparent = false;
@@ -137,6 +227,7 @@ ShaderManager::ShaderManager()
{
need_update = true;
blackbody_table_offset = TABLE_OFFSET_INVALID;
+ beckmann_table_offset = TABLE_OFFSET_INVALID;
}
ShaderManager::~ShaderManager()
@@ -257,6 +348,10 @@ void ShaderManager::device_update_common(Device *device, DeviceScene *dscene, Sc
flag |= SD_HAS_BSSRDF_BUMP;
if(shader->has_converter_blackbody)
has_converter_blackbody = true;
+ if(shader->volume_sampling_method == 1)
+ flag |= SD_VOLUME_EQUIANGULAR;
+ if(shader->volume_sampling_method == 2)
+ flag |= SD_VOLUME_MIS;
/* regular shader */
shader_flag[i++] = flag;
@@ -277,19 +372,28 @@ void ShaderManager::device_update_common(Device *device, DeviceScene *dscene, Sc
device->tex_alloc("__shader_flag", dscene->shader_flag);
/* blackbody lookup table */
- KernelBlackbody *kblackbody = &dscene->data.blackbody;
+ KernelTables *ktables = &dscene->data.tables;
if(has_converter_blackbody && blackbody_table_offset == TABLE_OFFSET_INVALID) {
vector<float> table = blackbody_table();
blackbody_table_offset = scene->lookup_tables->add_table(dscene, table);
- kblackbody->table_offset = (int)blackbody_table_offset;
+ ktables->blackbody_offset = (int)blackbody_table_offset;
}
else if(!has_converter_blackbody && blackbody_table_offset != TABLE_OFFSET_INVALID) {
scene->lookup_tables->remove_table(blackbody_table_offset);
blackbody_table_offset = TABLE_OFFSET_INVALID;
}
+ /* beckmann lookup table */
+ if(beckmann_table_offset == TABLE_OFFSET_INVALID) {
+ vector<float> table;
+ beckmann_table_build(table);
+ beckmann_table_offset = scene->lookup_tables->add_table(dscene, table);
+
+ ktables->beckmann_offset = (int)beckmann_table_offset;
+ }
+
/* integrator */
KernelIntegrator *kintegrator = &dscene->data.integrator;
kintegrator->use_volumes = has_volumes;
@@ -303,6 +407,11 @@ void ShaderManager::device_free_common(Device *device, DeviceScene *dscene, Scen
blackbody_table_offset = TABLE_OFFSET_INVALID;
}
+ if(beckmann_table_offset != TABLE_OFFSET_INVALID) {
+ scene->lookup_tables->remove_table(beckmann_table_offset);
+ beckmann_table_offset = TABLE_OFFSET_INVALID;
+ }
+
device->tex_free(dscene->shader_flag);
dscene->shader_flag.clear();
}
diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h
index dc18b385cc3..0ed6d2ddf01 100644
--- a/intern/cycles/render/shader.h
+++ b/intern/cycles/render/shader.h
@@ -68,6 +68,7 @@ public:
bool use_mis;
bool use_transparent_shadow;
bool heterogeneous_volume;
+ int volume_sampling_method;
/* synchronization */
bool need_update;
@@ -148,6 +149,7 @@ protected:
AttributeIDMap unique_attribute_id;
size_t blackbody_table_offset;
+ size_t beckmann_table_offset;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp
index 72bcdf966b5..d6094a4fa0a 100644
--- a/intern/cycles/render/tile.cpp
+++ b/intern/cycles/render/tile.cpp
@@ -202,7 +202,7 @@ list<Tile>::iterator TileManager::next_background_tile(int device, TileOrder til
case TILE_CENTER:
distx = centx - (cur_tile.x + cur_tile.w);
disty = centy - (cur_tile.y + cur_tile.h);
- distx = (int64_t) sqrt((double)distx * distx + disty * disty);
+ distx = (int64_t)sqrt((double)(distx * distx + disty * disty));
break;
case TILE_RIGHT_TO_LEFT:
distx = cordx - cur_tile.x;
diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt
index 79e2a078057..701ba313c79 100644
--- a/intern/cycles/util/CMakeLists.txt
+++ b/intern/cycles/util/CMakeLists.txt
@@ -16,6 +16,7 @@ set(SRC
util_opencl.cpp
util_path.cpp
util_string.cpp
+ util_simd.cpp
util_system.cpp
util_task.cpp
util_time.cpp
@@ -53,6 +54,9 @@ set(SRC_HEADERS
util_progress.h
util_set.h
util_simd.h
+ util_sseb.h
+ util_ssef.h
+ util_ssei.h
util_stats.h
util_string.h
util_system.h
diff --git a/intern/cycles/util/util_color.h b/intern/cycles/util/util_color.h
index b72cc6bc873..48e9e2d025f 100644
--- a/intern/cycles/util/util_color.h
+++ b/intern/cycles/util/util_color.h
@@ -26,6 +26,27 @@
CCL_NAMESPACE_BEGIN
+ccl_device uchar float_to_byte(float val)
+{
+ return ((val <= 0.0f) ? 0 : ((val > (1.0f - 0.5f / 255.0f)) ? 255 : (uchar)((255.0f * val) + 0.5f)));
+}
+
+ccl_device uchar4 color_float_to_byte(float3 c)
+{
+ uchar r, g, b;
+
+ r = float_to_byte(c.x);
+ g = float_to_byte(c.y);
+ b = float_to_byte(c.z);
+
+ return make_uchar4(r, g, b, 0);
+}
+
+ccl_device_inline float3 color_byte_to_float(uchar4 c)
+{
+ return make_float3(c.x*(1.0f/255.0f), c.y*(1.0f/255.0f), c.z*(1.0f/255.0f));
+}
+
ccl_device float color_srgb_to_scene_linear(float c)
{
if(c < 0.04045f)
@@ -155,28 +176,28 @@ ccl_device float3 color_srgb_to_scene_linear(float3 c)
* e2coeff = 2^(127/exponent - 127) * bias_coeff^(1/exponent), encoded as uint32_t
*/
template<unsigned exp, unsigned e2coeff>
-ccl_device_inline __m128 fastpow(const __m128 &arg)
+ccl_device_inline ssef fastpow(const ssef &arg)
{
- __m128 ret;
- ret = _mm_mul_ps(arg, _mm_castsi128_ps(_mm_set1_epi32(e2coeff)));
- ret = _mm_cvtepi32_ps(_mm_castps_si128(ret));
- ret = _mm_mul_ps(ret, _mm_castsi128_ps(_mm_set1_epi32(exp)));
- ret = _mm_castsi128_ps(_mm_cvtps_epi32(ret));
+ ssef ret;
+ ret = arg * cast(ssei(e2coeff));
+ ret = ssef(cast(ret));
+ ret = ret * cast(ssei(exp));
+ ret = cast(ssei(ret));
return ret;
}
/* Improve x ^ 1.0f/5.0f solution with Newton-Raphson method */
-ccl_device_inline __m128 improve_5throot_solution(const __m128 &old_result, const __m128 &x)
+ccl_device_inline ssef improve_5throot_solution(const ssef &old_result, const ssef &x)
{
- __m128 approx2 = _mm_mul_ps(old_result, old_result);
- __m128 approx4 = _mm_mul_ps(approx2, approx2);
- __m128 t = _mm_div_ps(x, approx4);
- __m128 summ = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(4.0f), old_result), t); /* fma */
- return _mm_mul_ps(summ, _mm_set1_ps(1.0f/5.0f));
+ ssef approx2 = old_result * old_result;
+ ssef approx4 = approx2 * approx2;
+ ssef t = x / approx4;
+ ssef summ = madd(ssef(4.0f), old_result, t);
+ return summ * ssef(1.0f/5.0f);
}
/* Calculate powf(x, 2.4). Working domain: 1e-10 < x < 1e+10 */
-ccl_device_inline __m128 fastpow24(const __m128 &arg)
+ccl_device_inline ssef fastpow24(const ssef &arg)
{
/* max, avg and |avg| errors were calculated in gcc without FMA instructions
* The final precision should be better than powf in glibc */
@@ -184,22 +205,22 @@ ccl_device_inline __m128 fastpow24(const __m128 &arg)
/* Calculate x^4/5, coefficient 0.994 was constructed manually to minimize avg error */
/* 0x3F4CCCCD = 4/5 */
/* 0x4F55A7FB = 2^(127/(4/5) - 127) * 0.994^(1/(4/5)) */
- __m128 x = fastpow<0x3F4CCCCD, 0x4F55A7FB>(arg); // error max = 0.17 avg = 0.0018 |avg| = 0.05
- __m128 arg2 = _mm_mul_ps(arg, arg);
- __m128 arg4 = _mm_mul_ps(arg2, arg2);
+ ssef x = fastpow<0x3F4CCCCD, 0x4F55A7FB>(arg); // error max = 0.17 avg = 0.0018 |avg| = 0.05
+ ssef arg2 = arg * arg;
+ ssef arg4 = arg2 * arg2;
x = improve_5throot_solution(x, arg4); /* error max = 0.018 avg = 0.0031 |avg| = 0.0031 */
x = improve_5throot_solution(x, arg4); /* error max = 0.00021 avg = 1.6e-05 |avg| = 1.6e-05 */
x = improve_5throot_solution(x, arg4); /* error max = 6.1e-07 avg = 5.2e-08 |avg| = 1.1e-07 */
- return _mm_mul_ps(x, _mm_mul_ps(x, x));
+ return x * (x * x);
}
-ccl_device __m128 color_srgb_to_scene_linear(const __m128 &c)
+ccl_device ssef color_srgb_to_scene_linear(const ssef &c)
{
- __m128 cmp = _mm_cmplt_ps(c, _mm_set1_ps(0.04045f));
- __m128 lt = _mm_max_ps(_mm_mul_ps(c, _mm_set1_ps(1.0f/12.92f)), _mm_set1_ps(0.0f));
- __m128 gtebase = _mm_mul_ps(_mm_add_ps(c, _mm_set1_ps(0.055f)), _mm_set1_ps(1.0f/1.055f)); /* fma */
- __m128 gte = fastpow24(gtebase);
- return blend(cmp, lt, gte);
+ sseb cmp = c < ssef(0.04045f);
+ ssef lt = max(c * ssef(1.0f/12.92f), ssef(0.0f));
+ ssef gtebase = (c + ssef(0.055f)) * ssef(1.0f/1.055f); /* fma */
+ ssef gte = fastpow24(gtebase);
+ return select(cmp, lt, gte);
}
#endif
diff --git a/intern/cycles/util/util_half.h b/intern/cycles/util/util_half.h
index da6fae79bb9..397133618be 100644
--- a/intern/cycles/util/util_half.h
+++ b/intern/cycles/util/util_half.h
@@ -68,18 +68,18 @@ ccl_device_inline void float4_store_half(half *h, float4 f, float scale)
}
#else
/* same as above with SSE */
- const __m128 mm_scale = _mm_set_ps1(scale);
- const __m128i mm_38800000 = _mm_set1_epi32(0x38800000);
- const __m128i mm_7FFF = _mm_set1_epi32(0x7FFF);
- const __m128i mm_7FFFFFFF = _mm_set1_epi32(0x7FFFFFFF);
- const __m128i mm_C8000000 = _mm_set1_epi32(0xC8000000);
-
- __m128 mm_fscale = _mm_mul_ps(load_m128(f), mm_scale);
- __m128i x = _mm_castps_si128(_mm_min_ps(_mm_max_ps(mm_fscale, _mm_set_ps1(0.0f)), _mm_set_ps1(65500.0f)));
- __m128i absolute = _mm_and_si128(x, mm_7FFFFFFF);
- __m128i Z = _mm_add_epi32(absolute, mm_C8000000);
- __m128i result = _mm_andnot_si128(_mm_cmplt_epi32(absolute, mm_38800000), Z);
- __m128i rh = _mm_and_si128(_mm_srai_epi32(result, 13), mm_7FFF);
+ const ssef mm_scale = ssef(scale);
+ const ssei mm_38800000 = ssei(0x38800000);
+ const ssei mm_7FFF = ssei(0x7FFF);
+ const ssei mm_7FFFFFFF = ssei(0x7FFFFFFF);
+ const ssei mm_C8000000 = ssei(0xC8000000);
+
+ ssef mm_fscale = load4f(f) * mm_scale;
+ ssei x = cast(min(max(mm_fscale, ssef(0.0f)), ssef(65500.0f)));
+ ssei absolute = x & mm_7FFFFFFF;
+ ssei Z = absolute + mm_C8000000;
+ ssei result = andnot(absolute < mm_38800000, Z);
+ ssei rh = (result >> 13) & mm_7FFF;
_mm_storel_pi((__m64*)h, _mm_castsi128_ps(_mm_packs_epi32(rh, rh)));
#endif
diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h
index ded75762cd2..fc6827573f8 100644
--- a/intern/cycles/util/util_math.h
+++ b/intern/cycles/util/util_math.h
@@ -622,11 +622,7 @@ ccl_device_inline bool is_zero(const float3 a)
ccl_device_inline float reduce_add(const float3 a)
{
-#ifdef __KERNEL_SSE__
- return (a.x + a.y + a.z);
-#else
return (a.x + a.y + a.z);
-#endif
}
ccl_device_inline float average(const float3 a)
diff --git a/intern/cycles/util/util_optimization.h b/intern/cycles/util/util_optimization.h
index f901513ec4b..5d0fea34761 100644
--- a/intern/cycles/util/util_optimization.h
+++ b/intern/cycles/util/util_optimization.h
@@ -65,10 +65,15 @@
#define WITH_CYCLES_OPTIMIZED_KERNEL_AVX
#endif
+#ifdef WITH_KERNEL_AVX2
+#define WITH_CYCLES_OPTIMIZED_KERNEL_AVX2
+#endif
+
/* MSVC 2008, no SSE41 (broken blendv intrinsic) and no AVX support */
#if defined(_MSC_VER) && (_MSC_VER < 1700)
#undef WITH_CYCLES_OPTIMIZED_KERNEL_SSE41
#undef WITH_CYCLES_OPTIMIZED_KERNEL_AVX
+#undef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2
#endif
#endif
@@ -101,6 +106,10 @@
/* SSE intrinsics headers */
#ifndef FREE_WINDOWS64
+#ifdef _MSC_VER
+#include <intrin.h>
+#else
+
#ifdef __KERNEL_SSE2__
#include <xmmintrin.h> /* SSE 1 */
#include <emmintrin.h> /* SSE 2 */
@@ -118,6 +127,12 @@
#include <smmintrin.h> /* SSE 4.1 */
#endif
+#ifdef __KERNEL_AVX__
+#include <immintrin.h> /* AVX */
+#endif
+
+#endif
+
#else
/* MinGW64 has conflicting declarations for these SSE headers in <windows.h>.
diff --git a/intern/cycles/util/util_simd.cpp b/intern/cycles/util/util_simd.cpp
new file mode 100644
index 00000000000..0436823e62a
--- /dev/null
+++ b/intern/cycles/util/util_simd.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2011-2013 Intel Corporation
+ * Modifications Copyright 2014, Blender Foundation.
+ *
+ * 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
+ */
+
+#ifdef WITH_KERNEL_SSE2
+
+#define __KERNEL_SSE2__
+#include "util_simd.h"
+
+CCL_NAMESPACE_BEGIN
+
+const __m128 _mm_lookupmask_ps[16] = {
+ _mm_castsi128_ps(_mm_set_epi32( 0, 0, 0, 0)),
+ _mm_castsi128_ps(_mm_set_epi32( 0, 0, 0,-1)),
+ _mm_castsi128_ps(_mm_set_epi32( 0, 0,-1, 0)),
+ _mm_castsi128_ps(_mm_set_epi32( 0, 0,-1,-1)),
+ _mm_castsi128_ps(_mm_set_epi32( 0,-1, 0, 0)),
+ _mm_castsi128_ps(_mm_set_epi32( 0,-1, 0,-1)),
+ _mm_castsi128_ps(_mm_set_epi32( 0,-1,-1, 0)),
+ _mm_castsi128_ps(_mm_set_epi32( 0,-1,-1,-1)),
+ _mm_castsi128_ps(_mm_set_epi32(-1, 0, 0, 0)),
+ _mm_castsi128_ps(_mm_set_epi32(-1, 0, 0,-1)),
+ _mm_castsi128_ps(_mm_set_epi32(-1, 0,-1, 0)),
+ _mm_castsi128_ps(_mm_set_epi32(-1, 0,-1,-1)),
+ _mm_castsi128_ps(_mm_set_epi32(-1,-1, 0, 0)),
+ _mm_castsi128_ps(_mm_set_epi32(-1,-1, 0,-1)),
+ _mm_castsi128_ps(_mm_set_epi32(-1,-1,-1, 0)),
+ _mm_castsi128_ps(_mm_set_epi32(-1,-1,-1,-1))
+};
+
+
+CCL_NAMESPACE_END
+
+#endif // WITH_KERNEL_SSE2
diff --git a/intern/cycles/util/util_simd.h b/intern/cycles/util/util_simd.h
index f0f37fa57aa..39506a6359b 100644
--- a/intern/cycles/util/util_simd.h
+++ b/intern/cycles/util/util_simd.h
@@ -1,7 +1,8 @@
/*
- * Copyright 2011-2013 Blender Foundation
+ * Copyright 2011-2013 Intel Corporation
+ * Modifications Copyright 2014, Blender Foundation.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * 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
*
@@ -14,263 +15,425 @@
* limitations under the License
*/
-#ifndef __UTIL_SIMD_H__
-#define __UTIL_SIMD_H__
+#ifndef __UTIL_SIMD_TYPES_H__
+#define __UTIL_SIMD_TYPES_H__
+
+#include <limits>
+
+#include "util_debug.h"
+#include "util_types.h"
CCL_NAMESPACE_BEGIN
#ifdef __KERNEL_SSE2__
-/* SSE shuffle utility functions */
+struct sseb;
+struct ssei;
+struct ssef;
+
+extern const __m128 _mm_lookupmask_ps[16];
+
+/* Special Types */
-#ifdef __KERNEL_SSSE3__
+static struct TrueTy {
+__forceinline operator bool( ) const { return true; }
+} True ccl_maybe_unused;
-/* faster version for SSSE3 */
-typedef __m128i shuffle_swap_t;
+static struct FalseTy {
+__forceinline operator bool( ) const { return false; }
+} False ccl_maybe_unused;
-ccl_device_inline const shuffle_swap_t shuffle_swap_identity(void)
+static struct NegInfTy
{
- return _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
-}
+__forceinline operator float ( ) const { return -std::numeric_limits<float>::infinity(); }
+__forceinline operator int ( ) const { return std::numeric_limits<int>::min(); }
+} neg_inf ccl_maybe_unused;
-ccl_device_inline const shuffle_swap_t shuffle_swap_swap(void)
+static struct PosInfTy
{
- return _mm_set_epi8(7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8);
+__forceinline operator float ( ) const { return std::numeric_limits<float>::infinity(); }
+__forceinline operator int ( ) const { return std::numeric_limits<int>::max(); }
+} inf ccl_maybe_unused, pos_inf ccl_maybe_unused;
+
+/* Intrinsics Functions */
+
+#if defined(__BMI__) && defined(__GNUC__)
+#define _tzcnt_u32 __tzcnt_u32
+#define _tzcnt_u64 __tzcnt_u64
+#endif
+
+#if defined(__LZCNT__)
+#define _lzcnt_u32 __lzcnt32
+#define _lzcnt_u64 __lzcnt64
+#endif
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+
+__forceinline int __popcnt(int in) {
+ return _mm_popcnt_u32(in);
}
-ccl_device_inline const __m128 shuffle_swap(const __m128& a, const shuffle_swap_t& shuf)
-{
- return _mm_castsi128_ps(_mm_shuffle_epi8(_mm_castps_si128(a), shuf));
+#if !defined(_MSC_VER)
+__forceinline unsigned int __popcnt(unsigned int in) {
+ return _mm_popcnt_u32(in);
+}
+#endif
+
+#if defined(__KERNEL_64_BIT__)
+__forceinline long long __popcnt(long long in) {
+ return _mm_popcnt_u64(in);
+}
+__forceinline size_t __popcnt(size_t in) {
+ return _mm_popcnt_u64(in);
+}
+#endif
+
+__forceinline int __bsf(int v) {
+#if defined(__KERNEL_AVX2__)
+ return _tzcnt_u32(v);
+#else
+ unsigned long r = 0; _BitScanForward(&r,v); return r;
+#endif
}
+__forceinline unsigned int __bsf(unsigned int v) {
+#if defined(__KERNEL_AVX2__)
+ return _tzcnt_u32(v);
#else
+ unsigned long r = 0; _BitScanForward(&r,v); return r;
+#endif
+}
-/* somewhat slower version for SSE2 */
-typedef int shuffle_swap_t;
+__forceinline int __bsr(int v) {
+ unsigned long r = 0; _BitScanReverse(&r,v); return r;
+}
-ccl_device_inline const shuffle_swap_t shuffle_swap_identity(void)
-{
- return 0;
+__forceinline int __btc(int v, int i) {
+ long r = v; _bittestandcomplement(&r,i); return r;
}
-ccl_device_inline const shuffle_swap_t shuffle_swap_swap(void)
-{
- return 1;
+__forceinline int __bts(int v, int i) {
+ long r = v; _bittestandset(&r,i); return r;
}
-ccl_device_inline const __m128 shuffle_swap(const __m128& a, shuffle_swap_t shuf)
-{
- /* shuffle value must be a constant, so we need to branch */
- if(shuf)
- return _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 3, 2));
- else
- return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 2, 1, 0));
+__forceinline int __btr(int v, int i) {
+ long r = v; _bittestandreset(&r,i); return r;
}
+__forceinline int bitscan(int v) {
+#if defined(__KERNEL_AVX2__)
+ return _tzcnt_u32(v);
+#else
+ return __bsf(v);
#endif
+}
-#ifdef __KERNEL_SSE41__
-ccl_device_inline void gen_idirsplat_swap(const __m128 &pn, const shuffle_swap_t &shuf_identity, const shuffle_swap_t &shuf_swap,
- const float3& idir, __m128 idirsplat[3], shuffle_swap_t shufflexyz[3])
+__forceinline int clz(const int x)
{
- const __m128 idirsplat_raw[] = { _mm_set_ps1(idir.x), _mm_set_ps1(idir.y), _mm_set_ps1(idir.z) };
- idirsplat[0] = _mm_xor_ps(idirsplat_raw[0], pn);
- idirsplat[1] = _mm_xor_ps(idirsplat_raw[1], pn);
- idirsplat[2] = _mm_xor_ps(idirsplat_raw[2], pn);
-
- const __m128 signmask = _mm_castsi128_ps(_mm_set1_epi32(0x80000000));
- const __m128 shuf_identity_f = _mm_castsi128_ps(shuf_identity);
- const __m128 shuf_swap_f = _mm_castsi128_ps(shuf_swap);
- shufflexyz[0] = _mm_castps_si128(_mm_blendv_ps(shuf_identity_f, shuf_swap_f, _mm_and_ps(idirsplat_raw[0], signmask)));
- shufflexyz[1] = _mm_castps_si128(_mm_blendv_ps(shuf_identity_f, shuf_swap_f, _mm_and_ps(idirsplat_raw[1], signmask)));
- shufflexyz[2] = _mm_castps_si128(_mm_blendv_ps(shuf_identity_f, shuf_swap_f, _mm_and_ps(idirsplat_raw[2], signmask)));
-}
+#if defined(__KERNEL_AVX2__)
+ return _lzcnt_u32(x);
#else
-ccl_device_inline void gen_idirsplat_swap(const __m128 &pn, const shuffle_swap_t &shuf_identity, const shuffle_swap_t &shuf_swap,
- const float3& idir, __m128 idirsplat[3], shuffle_swap_t shufflexyz[3])
-{
- idirsplat[0] = _mm_xor_ps(_mm_set_ps1(idir.x), pn);
- idirsplat[1] = _mm_xor_ps(_mm_set_ps1(idir.y), pn);
- idirsplat[2] = _mm_xor_ps(_mm_set_ps1(idir.z), pn);
-
- shufflexyz[0] = (idir.x >= 0)? shuf_identity: shuf_swap;
- shufflexyz[1] = (idir.y >= 0)? shuf_identity: shuf_swap;
- shufflexyz[2] = (idir.z >= 0)? shuf_identity: shuf_swap;
-}
+ if (UNLIKELY(x == 0)) return 32;
+ return 31 - __bsr(x);
#endif
+}
-template<size_t i0, size_t i1, size_t i2, size_t i3> ccl_device_inline const __m128 shuffle(const __m128& a, const __m128& b)
+__forceinline int __bscf(int& v)
{
- return _mm_shuffle_ps(a, b, _MM_SHUFFLE(i3, i2, i1, i0));
+ int i = __bsf(v);
+ v &= v-1;
+ return i;
}
-template<size_t i0, size_t i1, size_t i2, size_t i3> ccl_device_inline const __m128 shuffle(const __m128& a)
+__forceinline unsigned int __bscf(unsigned int& v)
{
- return _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(a), _MM_SHUFFLE(i3, i2, i1, i0)));
+ unsigned int i = __bsf(v);
+ v &= v-1;
+ return i;
}
-template<> __forceinline const __m128 shuffle<0, 1, 0, 1>(const __m128& a)
-{
- return _mm_movelh_ps(a, a);
+#if defined(__KERNEL_64_BIT__)
+
+__forceinline size_t __bsf(size_t v) {
+#if defined(__KERNEL_AVX2__)
+ return _tzcnt_u64(v);
+#else
+ unsigned long r = 0; _BitScanForward64(&r,v); return r;
+#endif
}
-template<> __forceinline const __m128 shuffle<2, 3, 2, 3>(const __m128& a)
-{
- return _mm_movehl_ps(a, a);
+__forceinline size_t __bsr(size_t v) {
+ unsigned long r = 0; _BitScanReverse64(&r,v); return r;
}
-template<size_t i0, size_t i1, size_t i2, size_t i3> ccl_device_inline const __m128i shuffle(const __m128i& a)
-{
- return _mm_shuffle_epi32(a, _MM_SHUFFLE(i3, i2, i1, i0));
+__forceinline size_t __btc(size_t v, size_t i) {
+ size_t r = v; _bittestandcomplement64((__int64*)&r,i); return r;
}
-template<size_t i0, size_t i1, size_t i2, size_t i3> ccl_device_inline const __m128i shuffle(const __m128i& a, const __m128i& b)
-{
- return _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(a), _mm_castsi128_ps(b), _MM_SHUFFLE(i3, i2, i1, i0)));
+__forceinline size_t __bts(size_t v, size_t i) {
+ __int64 r = v; _bittestandset64(&r,i); return r;
}
-/* Blend 2 vectors based on mask: (a[i] & mask[i]) | (b[i] & ~mask[i]) */
-#ifdef __KERNEL_SSE41__
-ccl_device_inline const __m128 blend(const __m128& mask, const __m128& a, const __m128& b)
-{
- return _mm_blendv_ps(b, a, mask);
+__forceinline size_t __btr(size_t v, size_t i) {
+ __int64 r = v; _bittestandreset64(&r,i); return r;
}
+
+__forceinline size_t bitscan(size_t v) {
+#if defined(__KERNEL_AVX2__)
+#if defined(__KERNEL_64_BIT__)
+ return _tzcnt_u64(v);
#else
-ccl_device_inline const __m128 blend(const __m128& mask, const __m128& a, const __m128& b)
-{
- return _mm_or_ps(_mm_and_ps(mask, a), _mm_andnot_ps(mask, b));
-}
+ return _tzcnt_u32(v);
#endif
+#else
+ return __bsf(v);
+#endif
+}
-/* calculate a*b+c (replacement for fused multiply-add on SSE CPUs) */
-ccl_device_inline const __m128 fma(const __m128& a, const __m128& b, const __m128& c)
+__forceinline size_t __bscf(size_t& v)
{
- return _mm_add_ps(_mm_mul_ps(a, b), c);
+ size_t i = __bsf(v);
+ v &= v-1;
+ return i;
}
-/* calculate a*b-c (replacement for fused multiply-subtract on SSE CPUs) */
-ccl_device_inline const __m128 fms(const __m128& a, const __m128& b, const __m128& c)
-{
- return _mm_sub_ps(_mm_mul_ps(a, b), c);
+#endif /* __KERNEL_64_BIT__ */
+
+#else /* _WIN32 */
+
+__forceinline unsigned int __popcnt(unsigned int in) {
+ int r = 0; asm ("popcnt %1,%0" : "=r"(r) : "r"(in)); return r;
}
-/* calculate -a*b+c (replacement for fused negated-multiply-subtract on SSE CPUs) */
-ccl_device_inline const __m128 fnma(const __m128& a, const __m128& b, const __m128& c)
-{
- return _mm_sub_ps(c, _mm_mul_ps(a, b));
+__forceinline int __bsf(int v) {
+ int r = 0; asm ("bsf %1,%0" : "=r"(r) : "r"(v)); return r;
}
-template<size_t N> ccl_device_inline const __m128 broadcast(const __m128& a)
-{
- return _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(a), _MM_SHUFFLE(N, N, N, N)));
+__forceinline int __bsr(int v) {
+ int r = 0; asm ("bsr %1,%0" : "=r"(r) : "r"(v)); return r;
}
-template<size_t N> ccl_device_inline const __m128i broadcast(const __m128i& a)
-{
- return _mm_shuffle_epi32(a, _MM_SHUFFLE(N, N, N, N));
+__forceinline int __btc(int v, int i) {
+ int r = 0; asm ("btc %1,%0" : "=r"(r) : "r"(i), "0"(v) : "flags" ); return r;
}
-ccl_device_inline const __m128 uint32_to_float(const __m128i &in)
-{
- __m128i a = _mm_srli_epi32(in, 16);
- __m128i b = _mm_and_si128(in, _mm_set1_epi32(0x0000ffff));
- __m128i c = _mm_or_si128(a, _mm_set1_epi32(0x53000000));
- __m128 d = _mm_cvtepi32_ps(b);
- __m128 e = _mm_sub_ps(_mm_castsi128_ps(c), _mm_castsi128_ps(_mm_set1_epi32(0x53000000)));
- return _mm_add_ps(e, d);
+__forceinline int __bts(int v, int i) {
+ int r = 0; asm ("bts %1,%0" : "=r"(r) : "r"(i), "0"(v) : "flags"); return r;
}
-template<size_t S1, size_t S2, size_t S3, size_t S4>
-ccl_device_inline const __m128 set_sign_bit(const __m128 &a)
-{
- return _mm_xor_ps(a, _mm_castsi128_ps(_mm_setr_epi32(S1 << 31, S2 << 31, S3 << 31, S4 << 31)));
+__forceinline int __btr(int v, int i) {
+ int r = 0; asm ("btr %1,%0" : "=r"(r) : "r"(i), "0"(v) : "flags"); return r;
}
-#ifdef __KERNEL_WITH_SSE_ALIGN__
-ccl_device_inline const __m128 load_m128(const float4 &vec)
-{
- return _mm_load_ps(&vec.x);
+#if defined(__KERNEL_64_BIT__) || defined(__APPLE__)
+__forceinline size_t __bsf(size_t v) {
+ size_t r = 0; asm ("bsf %1,%0" : "=r"(r) : "r"(v)); return r;
}
+#endif
-ccl_device_inline const __m128 load_m128(const float3 &vec)
-{
- return _mm_load_ps(&vec.x);
+__forceinline unsigned int __bsf(unsigned int v) {
+ unsigned int r = 0; asm ("bsf %1,%0" : "=r"(r) : "r"(v)); return r;
}
-#else
+__forceinline size_t __bsr(size_t v) {
+ size_t r = 0; asm ("bsr %1,%0" : "=r"(r) : "r"(v)); return r;
+}
-ccl_device_inline const __m128 load_m128(const float4 &vec)
-{
- return _mm_loadu_ps(&vec.x);
+__forceinline size_t __btc(size_t v, size_t i) {
+ size_t r = 0; asm ("btc %1,%0" : "=r"(r) : "r"(i), "0"(v) : "flags" ); return r;
}
-ccl_device_inline const __m128 load_m128(const float3 &vec)
-{
- return _mm_loadu_ps(&vec.x);
+__forceinline size_t __bts(size_t v, size_t i) {
+ size_t r = 0; asm ("bts %1,%0" : "=r"(r) : "r"(i), "0"(v) : "flags"); return r;
}
-#endif /* __KERNEL_WITH_SSE_ALIGN__ */
-ccl_device_inline const __m128 dot3_splat(const __m128& a, const __m128& b)
-{
-#ifdef __KERNEL_SSE41__
- return _mm_dp_ps(a, b, 0x7f);
+__forceinline size_t __btr(size_t v, size_t i) {
+ size_t r = 0; asm ("btr %1,%0" : "=r"(r) : "r"(i), "0"(v) : "flags"); return r;
+}
+
+__forceinline int bitscan(int v) {
+#if defined(__KERNEL_AVX2__)
+ return _tzcnt_u32(v);
#else
- __m128 t = _mm_mul_ps(a, b);
- return _mm_set1_ps(((float*)&t)[0] + ((float*)&t)[1] + ((float*)&t)[2]);
+ return __bsf(v);
#endif
}
-/* squared length taking only specified axes into account */
-template<size_t X, size_t Y, size_t Z, size_t W>
-ccl_device_inline float len_squared(const __m128& a)
-{
-#ifndef __KERNEL_SSE41__
- float4& t = (float4 &)a;
- return (X ? t.x * t.x : 0.0f) + (Y ? t.y * t.y : 0.0f) + (Z ? t.z * t.z : 0.0f) + (W ? t.w * t.w : 0.0f);
+__forceinline unsigned int bitscan(unsigned int v) {
+#if defined(__KERNEL_AVX2__)
+ return _tzcnt_u32(v);
#else
- return _mm_cvtss_f32(_mm_dp_ps(a, a, (X << 4) | (Y << 5) | (Z << 6) | (W << 7) | 0xf));
+ return __bsf(v);
#endif
}
-ccl_device_inline float dot3(const __m128& a, const __m128& b)
-{
-#ifdef __KERNEL_SSE41__
- return _mm_cvtss_f32(_mm_dp_ps(a, b, 0x7f));
+#if defined(__KERNEL_64_BIT__) || defined(__APPLE__)
+__forceinline size_t bitscan(size_t v) {
+#if defined(__KERNEL_AVX2__)
+#if defined(__KERNEL_64_BIT__)
+ return _tzcnt_u64(v);
+#else
+ return _tzcnt_u32(v);
+#endif
#else
- __m128 t = _mm_mul_ps(a, b);
- return ((float*)&t)[0] + ((float*)&t)[1] + ((float*)&t)[2];
+ return __bsf(v);
#endif
}
+#endif
-ccl_device_inline const __m128 len3_squared_splat(const __m128& a)
+__forceinline int clz(const int x)
{
- return dot3_splat(a, a);
+#if defined(__KERNEL_AVX2__)
+ return _lzcnt_u32(x);
+#else
+ if (UNLIKELY(x == 0)) return 32;
+ return 31 - __bsr(x);
+#endif
}
-ccl_device_inline float len3_squared(const __m128& a)
+__forceinline int __bscf(int& v)
{
- return dot3(a, a);
+ int i = bitscan(v);
+#if defined(__KERNEL_AVX2__)
+ v &= v-1;
+#else
+ v = __btc(v,i);
+#endif
+ return i;
}
-ccl_device_inline float len3(const __m128& a)
+__forceinline unsigned int __bscf(unsigned int& v)
{
- return _mm_cvtss_f32(_mm_sqrt_ss(dot3_splat(a, a)));
+ unsigned int i = bitscan(v);
+ v &= v-1;
+ return i;
}
-/* calculate shuffled cross product, useful when order of components does not matter */
-ccl_device_inline const __m128 cross_zxy(const __m128& a, const __m128& b)
+#if defined(__KERNEL_64_BIT__) || defined(__APPLE__)
+__forceinline size_t __bscf(size_t& v)
{
- return fms(a, shuffle<1, 2, 0, 3>(b), _mm_mul_ps(b, shuffle<1, 2, 0, 3>(a)));
+ size_t i = bitscan(v);
+#if defined(__KERNEL_AVX2__)
+ v &= v-1;
+#else
+ v = __btc(v,i);
+#endif
+ return i;
+}
+#endif
+
+#endif /* _WIN32 */
+
+static const unsigned int BITSCAN_NO_BIT_SET_32 = 32;
+static const size_t BITSCAN_NO_BIT_SET_64 = 64;
+
+/* Emulation of SSE4 functions with SSE3 */
+
+#if defined(__KERNEL_SSE3) && !defined(__KERNEL_SSE4__)
+
+#define _MM_FROUND_TO_NEAREST_INT 0x00
+#define _MM_FROUND_TO_NEG_INF 0x01
+#define _MM_FROUND_TO_POS_INF 0x02
+#define _MM_FROUND_TO_ZERO 0x03
+#define _MM_FROUND_CUR_DIRECTION 0x04
+
+#define _mm_blendv_ps __emu_mm_blendv_ps
+__forceinline __m128 _mm_blendv_ps( __m128 value, __m128 input, __m128 mask ) {
+ return _mm_or_ps(_mm_and_ps(mask, input), _mm_andnot_ps(mask, value));
+}
+
+#define _mm_blend_ps __emu_mm_blend_ps
+__forceinline __m128 _mm_blend_ps( __m128 value, __m128 input, const int mask ) {
+ assert(mask < 0x10); return _mm_blendv_ps(value, input, _mm_lookupmask_ps[mask]);
+}
+
+#define _mm_blendv_epi8 __emu_mm_blendv_epi8
+__forceinline __m128i _mm_blendv_epi8( __m128i value, __m128i input, __m128i mask ) {
+ return _mm_or_si128(_mm_and_si128(mask, input), _mm_andnot_si128(mask, value));
+}
+
+#define _mm_mullo_epi32 __emu_mm_mullo_epi32
+__forceinline __m128i _mm_mullo_epi32( __m128i value, __m128i input ) {
+ __m128i rvalue;
+ char* _r = (char*)(&rvalue + 1);
+ char* _v = (char*)(& value + 1);
+ char* _i = (char*)(& input + 1);
+ for ( ssize_t i = -16 ; i != 0 ; i += 4 ) *((int32*)(_r + i)) = *((int32*)(_v + i))* *((int32*)(_i + i));
+ return rvalue;
+}
+
+
+#define _mm_min_epi32 __emu_mm_min_epi32
+__forceinline __m128i _mm_min_epi32( __m128i value, __m128i input ) {
+ return _mm_blendv_epi8(input, value, _mm_cmplt_epi32(value, input));
+}
+
+#define _mm_max_epi32 __emu_mm_max_epi32
+__forceinline __m128i _mm_max_epi32( __m128i value, __m128i input ) {
+ return _mm_blendv_epi8(value, input, _mm_cmplt_epi32(value, input));
+}
+
+#define _mm_extract_epi32 __emu_mm_extract_epi32
+__forceinline int _mm_extract_epi32( __m128i input, const int index ) {
+ switch ( index ) {
+ case 0: return _mm_cvtsi128_si32(input);
+ case 1: return _mm_cvtsi128_si32(_mm_shuffle_epi32(input, _MM_SHUFFLE(1, 1, 1, 1)));
+ case 2: return _mm_cvtsi128_si32(_mm_shuffle_epi32(input, _MM_SHUFFLE(2, 2, 2, 2)));
+ case 3: return _mm_cvtsi128_si32(_mm_shuffle_epi32(input, _MM_SHUFFLE(3, 3, 3, 3)));
+ default: assert(false); return 0;
+ }
+}
+
+#define _mm_insert_epi32 __emu_mm_insert_epi32
+__forceinline __m128i _mm_insert_epi32( __m128i value, int input, const int index ) {
+ assert(index >= 0 && index < 4); ((int*)&value)[index] = input; return value;
}
-ccl_device_inline const __m128 cross(const __m128& a, const __m128& b)
+#define _mm_extract_ps __emu_mm_extract_ps
+__forceinline int _mm_extract_ps( __m128 input, const int index ) {
+ int32* ptr = (int32*)&input; return ptr[index];
+}
+
+#define _mm_insert_ps __emu_mm_insert_ps
+__forceinline __m128 _mm_insert_ps( __m128 value, __m128 input, const int index )
+{ assert(index < 0x100); ((float*)&value)[(index >> 4)&0x3] = ((float*)&input)[index >> 6]; return _mm_andnot_ps(_mm_lookupmask_ps[index&0xf], value); }
+
+#define _mm_round_ps __emu_mm_round_ps
+__forceinline __m128 _mm_round_ps( __m128 value, const int flags )
{
- return shuffle<1, 2, 0, 3>(cross_zxy(a, b));
+ switch ( flags )
+ {
+ case _MM_FROUND_TO_NEAREST_INT: return _mm_cvtepi32_ps(_mm_cvtps_epi32(value));
+ case _MM_FROUND_TO_NEG_INF : return _mm_cvtepi32_ps(_mm_cvtps_epi32(_mm_add_ps(value, _mm_set1_ps(-0.5f))));
+ case _MM_FROUND_TO_POS_INF : return _mm_cvtepi32_ps(_mm_cvtps_epi32(_mm_add_ps(value, _mm_set1_ps( 0.5f))));
+ case _MM_FROUND_TO_ZERO : return _mm_cvtepi32_ps(_mm_cvttps_epi32(value));
+ }
+ return value;
+}
+
+#ifdef _M_X64
+#define _mm_insert_epi64 __emu_mm_insert_epi64
+__forceinline __m128i _mm_insert_epi64( __m128i value, __int64 input, const int index ) {
+ assert(size_t(index) < 4); ((__int64*)&value)[index] = input; return value;
+}
+
+#define _mm_extract_epi64 __emu_mm_extract_epi64
+__forceinline __int64 _mm_extract_epi64( __m128i input, const int index ) {
+ assert(size_t(index) < 2);
+ return index == 0 ? _mm_cvtsi128_si64x(input) : _mm_cvtsi128_si64x(_mm_unpackhi_epi64(input, input));
}
+#endif
+
+#endif
#endif /* __KERNEL_SSE2__ */
CCL_NAMESPACE_END
-#endif /* __UTIL_SIMD_H__ */
+#include "util_math.h"
+#include "util_sseb.h"
+#include "util_ssei.h"
+#include "util_ssef.h"
+
+#endif /* __UTIL_SIMD_TYPES_H__ */
diff --git a/intern/cycles/util/util_sseb.h b/intern/cycles/util/util_sseb.h
new file mode 100644
index 00000000000..be510256dd3
--- /dev/null
+++ b/intern/cycles/util/util_sseb.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2011-2013 Intel Corporation
+ * Modifications Copyright 2014, Blender Foundation.
+ *
+ * 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
+ */
+
+#ifndef __UTIL_SSEB_H__
+#define __UTIL_SSEB_H__
+
+CCL_NAMESPACE_BEGIN
+
+#ifdef __KERNEL_SSE2__
+
+/*! 4-wide SSE bool type. */
+struct sseb
+{
+ typedef sseb Mask; // mask type
+ typedef ssei Int; // int type
+ typedef ssef Float; // float type
+
+ enum { size = 4 }; // number of SIMD elements
+ union { __m128 m128; int32_t v[4]; }; // data
+
+ ////////////////////////////////////////////////////////////////////////////////
+ /// Constructors, Assignment & Cast Operators
+ ////////////////////////////////////////////////////////////////////////////////
+
+ __forceinline sseb ( ) {}
+ __forceinline sseb ( const sseb& other ) { m128 = other.m128; }
+ __forceinline sseb& operator=( const sseb& other ) { m128 = other.m128; return *this; }
+
+ __forceinline sseb( const __m128 input ) : m128(input) {}
+ __forceinline operator const __m128&( void ) const { return m128; }
+ __forceinline operator const __m128i( void ) const { return _mm_castps_si128(m128); }
+ __forceinline operator const __m128d( void ) const { return _mm_castps_pd(m128); }
+
+ __forceinline sseb ( bool a )
+ : m128(_mm_lookupmask_ps[(size_t(a) << 3) | (size_t(a) << 2) | (size_t(a) << 1) | size_t(a)]) {}
+ __forceinline sseb ( bool a, bool b)
+ : m128(_mm_lookupmask_ps[(size_t(b) << 3) | (size_t(a) << 2) | (size_t(b) << 1) | size_t(a)]) {}
+ __forceinline sseb ( bool a, bool b, bool c, bool d)
+ : m128(_mm_lookupmask_ps[(size_t(d) << 3) | (size_t(c) << 2) | (size_t(b) << 1) | size_t(a)]) {}
+ __forceinline sseb(int mask) {
+ assert(mask >= 0 && mask < 16);
+ m128 = _mm_lookupmask_ps[mask];
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ /// Constants
+ ////////////////////////////////////////////////////////////////////////////////
+
+ __forceinline sseb( FalseTy ) : m128(_mm_setzero_ps()) {}
+ __forceinline sseb( TrueTy ) : m128(_mm_castsi128_ps(_mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128()))) {}
+
+ ////////////////////////////////////////////////////////////////////////////////
+ /// Array Access
+ ////////////////////////////////////////////////////////////////////////////////
+
+ __forceinline bool operator []( const size_t i ) const { assert(i < 4); return (_mm_movemask_ps(m128) >> i) & 1; }
+ __forceinline int32_t& operator []( const size_t i ) { assert(i < 4); return v[i]; }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+/// Unary Operators
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline const sseb operator !( const sseb& a ) { return _mm_xor_ps(a, sseb(True)); }
+
+////////////////////////////////////////////////////////////////////////////////
+/// Binary Operators
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline const sseb operator &( const sseb& a, const sseb& b ) { return _mm_and_ps(a, b); }
+__forceinline const sseb operator |( const sseb& a, const sseb& b ) { return _mm_or_ps (a, b); }
+__forceinline const sseb operator ^( const sseb& a, const sseb& b ) { return _mm_xor_ps(a, b); }
+
+////////////////////////////////////////////////////////////////////////////////
+/// Assignment Operators
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline const sseb operator &=( sseb& a, const sseb& b ) { return a = a & b; }
+__forceinline const sseb operator |=( sseb& a, const sseb& b ) { return a = a | b; }
+__forceinline const sseb operator ^=( sseb& a, const sseb& b ) { return a = a ^ b; }
+
+////////////////////////////////////////////////////////////////////////////////
+/// Comparison Operators + Select
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline const sseb operator !=( const sseb& a, const sseb& b ) { return _mm_xor_ps(a, b); }
+__forceinline const sseb operator ==( const sseb& a, const sseb& b ) { return _mm_castsi128_ps(_mm_cmpeq_epi32(a, b)); }
+
+__forceinline const sseb select( const sseb& m, const sseb& t, const sseb& f ) {
+#if defined(__KERNEL_SSE41__)
+ return _mm_blendv_ps(f, t, m);
+#else
+ return _mm_or_ps(_mm_and_ps(m, t), _mm_andnot_ps(m, f));
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Movement/Shifting/Shuffling Functions
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline const sseb unpacklo( const sseb& a, const sseb& b ) { return _mm_unpacklo_ps(a, b); }
+__forceinline const sseb unpackhi( const sseb& a, const sseb& b ) { return _mm_unpackhi_ps(a, b); }
+
+template<size_t i0, size_t i1, size_t i2, size_t i3> __forceinline const sseb shuffle( const sseb& a ) {
+ return _mm_shuffle_epi32(a, _MM_SHUFFLE(i3, i2, i1, i0));
+}
+
+template<size_t i0, size_t i1, size_t i2, size_t i3> __forceinline const sseb shuffle( const sseb& a, const sseb& b ) {
+ return _mm_shuffle_ps(a, b, _MM_SHUFFLE(i3, i2, i1, i0));
+}
+
+#if defined(__KERNEL_SSE3__)
+template<> __forceinline const sseb shuffle<0, 0, 2, 2>( const sseb& a ) { return _mm_moveldup_ps(a); }
+template<> __forceinline const sseb shuffle<1, 1, 3, 3>( const sseb& a ) { return _mm_movehdup_ps(a); }
+template<> __forceinline const sseb shuffle<0, 1, 0, 1>( const sseb& a ) { return _mm_castpd_ps(_mm_movedup_pd (a)); }
+#endif
+
+#if defined(__KERNEL_SSE41__)
+template<size_t dst, size_t src, size_t clr> __forceinline const sseb insert( const sseb& a, const sseb& b ) { return _mm_insert_ps(a, b, (dst << 4) | (src << 6) | clr); }
+template<size_t dst, size_t src> __forceinline const sseb insert( const sseb& a, const sseb& b ) { return insert<dst, src, 0>(a, b); }
+template<size_t dst> __forceinline const sseb insert( const sseb& a, const bool b ) { return insert<dst,0>(a, sseb(b)); }
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+/// Reduction Operations
+////////////////////////////////////////////////////////////////////////////////
+
+#if defined(__KERNEL_SSE41__)
+__forceinline size_t popcnt( const sseb& a ) { return __popcnt(_mm_movemask_ps(a)); }
+#else
+__forceinline size_t popcnt( const sseb& a ) { return bool(a[0])+bool(a[1])+bool(a[2])+bool(a[3]); }
+#endif
+
+__forceinline bool reduce_and( const sseb& a ) { return _mm_movemask_ps(a) == 0xf; }
+__forceinline bool reduce_or ( const sseb& a ) { return _mm_movemask_ps(a) != 0x0; }
+__forceinline bool all ( const sseb& b ) { return _mm_movemask_ps(b) == 0xf; }
+__forceinline bool any ( const sseb& b ) { return _mm_movemask_ps(b) != 0x0; }
+__forceinline bool none ( const sseb& b ) { return _mm_movemask_ps(b) == 0x0; }
+
+__forceinline size_t movemask( const sseb& a ) { return _mm_movemask_ps(a); }
+
+#endif
+
+CCL_NAMESPACE_END
+
+#endif
+
diff --git a/intern/cycles/util/util_ssef.h b/intern/cycles/util/util_ssef.h
new file mode 100644
index 00000000000..f4236cc616e
--- /dev/null
+++ b/intern/cycles/util/util_ssef.h
@@ -0,0 +1,588 @@
+/*
+ * Copyright 2011-2013 Intel Corporation
+ * Modifications Copyright 2014, Blender Foundation.
+ *
+ * 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
+ */
+
+#ifndef __UTIL_SSEF_H__
+#define __UTIL_SSEF_H__
+
+CCL_NAMESPACE_BEGIN
+
+#ifdef __KERNEL_SSE2__
+
+/*! 4-wide SSE float type. */
+struct ssef
+{
+ typedef sseb Mask; // mask type
+ typedef ssei Int; // int type
+ typedef ssef Float; // float type
+
+ enum { size = 4 }; // number of SIMD elements
+ union { __m128 m128; float f[4]; int i[4]; }; // data
+
+ ////////////////////////////////////////////////////////////////////////////////
+ /// Constructors, Assignment & Cast Operators
+ ////////////////////////////////////////////////////////////////////////////////
+
+ __forceinline ssef () {}
+ __forceinline ssef (const ssef& other) { m128 = other.m128; }
+ __forceinline ssef& operator=(const ssef& other) { m128 = other.m128; return *this; }
+
+ __forceinline ssef(const __m128 a) : m128(a) {}
+ __forceinline operator const __m128&(void) const { return m128; }
+ __forceinline operator __m128&(void) { return m128; }
+
+ __forceinline ssef (float a) : m128(_mm_set1_ps(a)) {}
+ __forceinline ssef (float a, float b, float c, float d) : m128(_mm_setr_ps(a, b, c, d)) {}
+
+ __forceinline explicit ssef(const __m128i a) : m128(_mm_cvtepi32_ps(a)) {}
+
+ ////////////////////////////////////////////////////////////////////////////////
+ /// Loads and Stores
+ ////////////////////////////////////////////////////////////////////////////////
+
+#if defined(__KERNEL_AVX__)
+ static __forceinline ssef broadcast(const void* const a) { return _mm_broadcast_ss((float*)a); }
+#else
+ static __forceinline ssef broadcast(const void* const a) { return _mm_set1_ps(*(float*)a); }
+#endif
+
+ ////////////////////////////////////////////////////////////////////////////////
+ /// Array Access
+ ////////////////////////////////////////////////////////////////////////////////
+
+ __forceinline const float& operator [](const size_t i) const { assert(i < 4); return f[i]; }
+ __forceinline float& operator [](const size_t i) { assert(i < 4); return f[i]; }
+};
+
+
+////////////////////////////////////////////////////////////////////////////////
+/// Unary Operators
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline const ssef cast (const __m128i& a) { return _mm_castsi128_ps(a); }
+__forceinline const ssef operator +(const ssef& a) { return a; }
+__forceinline const ssef operator -(const ssef& a) { return _mm_xor_ps(a.m128, _mm_castsi128_ps(_mm_set1_epi32(0x80000000))); }
+__forceinline const ssef abs (const ssef& a) { return _mm_and_ps(a.m128, _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff))); }
+#if defined(__KERNEL_SSE41__)
+__forceinline const ssef sign (const ssef& a) { return _mm_blendv_ps(ssef(1.0f), -ssef(1.0f), _mm_cmplt_ps(a,ssef(0.0f))); }
+#endif
+__forceinline const ssef signmsk (const ssef& a) { return _mm_and_ps(a.m128,_mm_castsi128_ps(_mm_set1_epi32(0x80000000))); }
+
+__forceinline const ssef rcp (const ssef& a) {
+ const ssef r = _mm_rcp_ps(a.m128);
+ return _mm_sub_ps(_mm_add_ps(r, r), _mm_mul_ps(_mm_mul_ps(r, r), a));
+}
+__forceinline const ssef sqr (const ssef& a) { return _mm_mul_ps(a,a); }
+__forceinline const ssef mm_sqrt(const ssef& a) { return _mm_sqrt_ps(a.m128); }
+__forceinline const ssef rsqrt(const ssef& a) {
+ const ssef r = _mm_rsqrt_ps(a.m128);
+ return _mm_add_ps(_mm_mul_ps(_mm_set_ps(1.5f, 1.5f, 1.5f, 1.5f), r),
+ _mm_mul_ps(_mm_mul_ps(_mm_mul_ps(a, _mm_set_ps(-0.5f, -0.5f, -0.5f, -0.5f)), r), _mm_mul_ps(r, r)));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Binary Operators
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline const ssef operator +(const ssef& a, const ssef& b) { return _mm_add_ps(a.m128, b.m128); }
+__forceinline const ssef operator +(const ssef& a, const float& b) { return a + ssef(b); }
+__forceinline const ssef operator +(const float& a, const ssef& b) { return ssef(a) + b; }
+
+__forceinline const ssef operator -(const ssef& a, const ssef& b) { return _mm_sub_ps(a.m128, b.m128); }
+__forceinline const ssef operator -(const ssef& a, const float& b) { return a - ssef(b); }
+__forceinline const ssef operator -(const float& a, const ssef& b) { return ssef(a) - b; }
+
+__forceinline const ssef operator *(const ssef& a, const ssef& b) { return _mm_mul_ps(a.m128, b.m128); }
+__forceinline const ssef operator *(const ssef& a, const float& b) { return a * ssef(b); }
+__forceinline const ssef operator *(const float& a, const ssef& b) { return ssef(a) * b; }
+
+__forceinline const ssef operator /(const ssef& a, const ssef& b) { return _mm_div_ps(a.m128,b.m128); }
+__forceinline const ssef operator /(const ssef& a, const float& b) { return a/ssef(b); }
+__forceinline const ssef operator /(const float& a, const ssef& b) { return ssef(a)/b; }
+
+__forceinline const ssef operator^(const ssef& a, const ssef& b) { return _mm_xor_ps(a.m128,b.m128); }
+__forceinline const ssef operator^(const ssef& a, const ssei& b) { return _mm_xor_ps(a.m128,_mm_castsi128_ps(b.m128)); }
+
+__forceinline const ssef operator&(const ssef& a, const ssef& b) { return _mm_and_ps(a.m128,b.m128); }
+__forceinline const ssef operator&(const ssef& a, const ssei& b) { return _mm_and_ps(a.m128,_mm_castsi128_ps(b.m128)); }
+
+__forceinline const ssef andnot(const ssef& a, const ssef& b) { return _mm_andnot_ps(a.m128,b.m128); }
+
+__forceinline const ssef min(const ssef& a, const ssef& b) { return _mm_min_ps(a.m128,b.m128); }
+__forceinline const ssef min(const ssef& a, const float& b) { return _mm_min_ps(a.m128,ssef(b)); }
+__forceinline const ssef min(const float& a, const ssef& b) { return _mm_min_ps(ssef(a),b.m128); }
+
+__forceinline const ssef max(const ssef& a, const ssef& b) { return _mm_max_ps(a.m128,b.m128); }
+__forceinline const ssef max(const ssef& a, const float& b) { return _mm_max_ps(a.m128,ssef(b)); }
+__forceinline const ssef max(const float& a, const ssef& b) { return _mm_max_ps(ssef(a),b.m128); }
+
+#if defined(__KERNEL_SSE41__)
+__forceinline ssef mini(const ssef& a, const ssef& b) {
+ const ssei ai = _mm_castps_si128(a);
+ const ssei bi = _mm_castps_si128(b);
+ const ssei ci = _mm_min_epi32(ai,bi);
+ return _mm_castsi128_ps(ci);
+}
+#endif
+
+#if defined(__KERNEL_SSE41__)
+__forceinline ssef maxi(const ssef& a, const ssef& b) {
+ const ssei ai = _mm_castps_si128(a);
+ const ssei bi = _mm_castps_si128(b);
+ const ssei ci = _mm_max_epi32(ai,bi);
+ return _mm_castsi128_ps(ci);
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+/// Ternary Operators
+////////////////////////////////////////////////////////////////////////////////
+
+#if defined(__KERNEL_AVX2__)
+__forceinline const ssef madd (const ssef& a, const ssef& b, const ssef& c) { return _mm_fmadd_ps(a,b,c); }
+__forceinline const ssef msub (const ssef& a, const ssef& b, const ssef& c) { return _mm_fmsub_ps(a,b,c); }
+__forceinline const ssef nmadd(const ssef& a, const ssef& b, const ssef& c) { return _mm_fnmadd_ps(a,b,c); }
+__forceinline const ssef nmsub(const ssef& a, const ssef& b, const ssef& c) { return _mm_fnmsub_ps(a,b,c); }
+#else
+__forceinline const ssef madd (const ssef& a, const ssef& b, const ssef& c) { return a*b+c; }
+__forceinline const ssef msub (const ssef& a, const ssef& b, const ssef& c) { return a*b-c; }
+__forceinline const ssef nmadd(const ssef& a, const ssef& b, const ssef& c) { return -a*b-c;}
+__forceinline const ssef nmsub(const ssef& a, const ssef& b, const ssef& c) { return c-a*b; }
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+/// Assignment Operators
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline ssef& operator +=(ssef& a, const ssef& b) { return a = a + b; }
+__forceinline ssef& operator +=(ssef& a, const float& b) { return a = a + b; }
+
+__forceinline ssef& operator -=(ssef& a, const ssef& b) { return a = a - b; }
+__forceinline ssef& operator -=(ssef& a, const float& b) { return a = a - b; }
+
+__forceinline ssef& operator *=(ssef& a, const ssef& b) { return a = a * b; }
+__forceinline ssef& operator *=(ssef& a, const float& b) { return a = a * b; }
+
+__forceinline ssef& operator /=(ssef& a, const ssef& b) { return a = a / b; }
+__forceinline ssef& operator /=(ssef& a, const float& b) { return a = a / b; }
+
+////////////////////////////////////////////////////////////////////////////////
+/// Comparison Operators + Select
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline const sseb operator ==(const ssef& a, const ssef& b) { return _mm_cmpeq_ps(a.m128, b.m128); }
+__forceinline const sseb operator ==(const ssef& a, const float& b) { return a == ssef(b); }
+__forceinline const sseb operator ==(const float& a, const ssef& b) { return ssef(a) == b; }
+
+__forceinline const sseb operator !=(const ssef& a, const ssef& b) { return _mm_cmpneq_ps(a.m128, b.m128); }
+__forceinline const sseb operator !=(const ssef& a, const float& b) { return a != ssef(b); }
+__forceinline const sseb operator !=(const float& a, const ssef& b) { return ssef(a) != b; }
+
+__forceinline const sseb operator <(const ssef& a, const ssef& b) { return _mm_cmplt_ps(a.m128, b.m128); }
+__forceinline const sseb operator <(const ssef& a, const float& b) { return a < ssef(b); }
+__forceinline const sseb operator <(const float& a, const ssef& b) { return ssef(a) < b; }
+
+__forceinline const sseb operator >=(const ssef& a, const ssef& b) { return _mm_cmpnlt_ps(a.m128, b.m128); }
+__forceinline const sseb operator >=(const ssef& a, const float& b) { return a >= ssef(b); }
+__forceinline const sseb operator >=(const float& a, const ssef& b) { return ssef(a) >= b; }
+
+__forceinline const sseb operator >(const ssef& a, const ssef& b) { return _mm_cmpnle_ps(a.m128, b.m128); }
+__forceinline const sseb operator >(const ssef& a, const float& b) { return a > ssef(b); }
+__forceinline const sseb operator >(const float& a, const ssef& b) { return ssef(a) > b; }
+
+__forceinline const sseb operator <=(const ssef& a, const ssef& b) { return _mm_cmple_ps(a.m128, b.m128); }
+__forceinline const sseb operator <=(const ssef& a, const float& b) { return a <= ssef(b); }
+__forceinline const sseb operator <=(const float& a, const ssef& b) { return ssef(a) <= b; }
+
+__forceinline const ssef select(const sseb& m, const ssef& t, const ssef& f) {
+#ifdef __KERNEL_SSE41__
+ return _mm_blendv_ps(f, t, m);
+#else
+ return _mm_or_ps(_mm_and_ps(m, t), _mm_andnot_ps(m, f));
+#endif
+}
+
+__forceinline const ssef select(const ssef& m, const ssef& t, const ssef& f) {
+#ifdef __KERNEL_SSE41__
+ return _mm_blendv_ps(f, t, m);
+#else
+ return _mm_or_ps(_mm_and_ps(m, t), _mm_andnot_ps(m, f));
+#endif
+}
+
+__forceinline const ssef select(const int mask, const ssef& t, const ssef& f) {
+#if defined(__KERNEL_SSE41__) && ((!defined(__clang__) && !defined(_MSC_VER)) || defined(__INTEL_COMPILER))
+ return _mm_blend_ps(f, t, mask);
+#else
+ return select(sseb(mask),t,f);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Rounding Functions
+////////////////////////////////////////////////////////////////////////////////
+
+#if defined(__KERNEL_SSE41__)
+__forceinline const ssef round_even(const ssef& a) { return _mm_round_ps(a, _MM_FROUND_TO_NEAREST_INT); }
+__forceinline const ssef round_down(const ssef& a) { return _mm_round_ps(a, _MM_FROUND_TO_NEG_INF ); }
+__forceinline const ssef round_up (const ssef& a) { return _mm_round_ps(a, _MM_FROUND_TO_POS_INF ); }
+__forceinline const ssef round_zero(const ssef& a) { return _mm_round_ps(a, _MM_FROUND_TO_ZERO ); }
+__forceinline const ssef floor (const ssef& a) { return _mm_round_ps(a, _MM_FROUND_TO_NEG_INF ); }
+__forceinline const ssef ceil (const ssef& a) { return _mm_round_ps(a, _MM_FROUND_TO_POS_INF ); }
+#endif
+
+__forceinline ssei truncatei(const ssef& a) {
+ return _mm_cvttps_epi32(a.m128);
+}
+
+__forceinline ssei floori(const ssef& a) {
+#if defined(__KERNEL_SSE41__)
+ return ssei(floor(a));
+#else
+ return ssei(a-ssef(0.5f));
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Movement/Shifting/Shuffling Functions
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline ssef unpacklo(const ssef& a, const ssef& b) { return _mm_unpacklo_ps(a.m128, b.m128); }
+__forceinline ssef unpackhi(const ssef& a, const ssef& b) { return _mm_unpackhi_ps(a.m128, b.m128); }
+
+template<size_t i0, size_t i1, size_t i2, size_t i3> __forceinline const ssef shuffle(const ssef& b) {
+ return _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(b), _MM_SHUFFLE(i3, i2, i1, i0)));
+}
+
+template<size_t i0, size_t i1, size_t i2, size_t i3> __forceinline const ssef shuffle(const ssef& a, const ssef& b) {
+ return _mm_shuffle_ps(a, b, _MM_SHUFFLE(i3, i2, i1, i0));
+}
+
+#if defined(__KERNEL_SSSE3__)
+__forceinline const ssef shuffle8(const ssef& a, const ssei& shuf) {
+ return _mm_castsi128_ps(_mm_shuffle_epi8(_mm_castps_si128(a), shuf));
+}
+#endif
+
+#if defined(__KERNEL_SSE3__)
+template<> __forceinline const ssef shuffle<0, 0, 2, 2>(const ssef& b) { return _mm_moveldup_ps(b); }
+template<> __forceinline const ssef shuffle<1, 1, 3, 3>(const ssef& b) { return _mm_movehdup_ps(b); }
+template<> __forceinline const ssef shuffle<0, 1, 0, 1>(const ssef& b) { return _mm_castpd_ps(_mm_movedup_pd(_mm_castps_pd(b))); }
+#endif
+
+template<size_t i0> __forceinline const ssef shuffle(const ssef& b) {
+ return shuffle<i0,i0,i0,i0>(b);
+}
+
+#if defined(__KERNEL_SSE41__) && !defined(__GNUC__)
+template<size_t i> __forceinline float extract (const ssef& a) { return _mm_cvtss_f32(_mm_extract_ps(a,i)); }
+#else
+template<size_t i> __forceinline float extract (const ssef& a) { return _mm_cvtss_f32(shuffle<i,i,i,i>(a)); }
+#endif
+template<> __forceinline float extract<0>(const ssef& a) { return _mm_cvtss_f32(a); }
+
+#if defined(__KERNEL_SSE41__)
+template<size_t dst, size_t src, size_t clr> __forceinline const ssef insert(const ssef& a, const ssef& b) { return _mm_insert_ps(a, b,(dst << 4) |(src << 6) | clr); }
+template<size_t dst, size_t src> __forceinline const ssef insert(const ssef& a, const ssef& b) { return insert<dst, src, 0>(a, b); }
+template<size_t dst> __forceinline const ssef insert(const ssef& a, const float b) { return insert<dst, 0>(a, _mm_set_ss(b)); }
+#else
+template<size_t dst> __forceinline const ssef insert(const ssef& a, const float b) { ssef c = a; c[dst] = b; return c; }
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+/// Transpose
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline void transpose(const ssef& r0, const ssef& r1, const ssef& r2, const ssef& r3, ssef& c0, ssef& c1, ssef& c2, ssef& c3)
+{
+ ssef l02 = unpacklo(r0,r2);
+ ssef h02 = unpackhi(r0,r2);
+ ssef l13 = unpacklo(r1,r3);
+ ssef h13 = unpackhi(r1,r3);
+ c0 = unpacklo(l02,l13);
+ c1 = unpackhi(l02,l13);
+ c2 = unpacklo(h02,h13);
+ c3 = unpackhi(h02,h13);
+}
+
+__forceinline void transpose(const ssef& r0, const ssef& r1, const ssef& r2, const ssef& r3, ssef& c0, ssef& c1, ssef& c2)
+{
+ ssef l02 = unpacklo(r0,r2);
+ ssef h02 = unpackhi(r0,r2);
+ ssef l13 = unpacklo(r1,r3);
+ ssef h13 = unpackhi(r1,r3);
+ c0 = unpacklo(l02,l13);
+ c1 = unpackhi(l02,l13);
+ c2 = unpacklo(h02,h13);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Reductions
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline const ssef vreduce_min(const ssef& v) { ssef h = min(shuffle<1,0,3,2>(v),v); return min(shuffle<2,3,0,1>(h),h); }
+__forceinline const ssef vreduce_max(const ssef& v) { ssef h = max(shuffle<1,0,3,2>(v),v); return max(shuffle<2,3,0,1>(h),h); }
+__forceinline const ssef vreduce_add(const ssef& v) { ssef h = shuffle<1,0,3,2>(v) + v ; return shuffle<2,3,0,1>(h) + h ; }
+
+__forceinline float reduce_min(const ssef& v) { return _mm_cvtss_f32(vreduce_min(v)); }
+__forceinline float reduce_max(const ssef& v) { return _mm_cvtss_f32(vreduce_max(v)); }
+__forceinline float reduce_add(const ssef& v) { return _mm_cvtss_f32(vreduce_add(v)); }
+
+__forceinline size_t select_min(const ssef& v) { return __bsf(movemask(v == vreduce_min(v))); }
+__forceinline size_t select_max(const ssef& v) { return __bsf(movemask(v == vreduce_max(v))); }
+
+__forceinline size_t select_min(const sseb& valid, const ssef& v) { const ssef a = select(valid,v,ssef(pos_inf)); return __bsf(movemask(valid &(a == vreduce_min(a)))); }
+__forceinline size_t select_max(const sseb& valid, const ssef& v) { const ssef a = select(valid,v,ssef(neg_inf)); return __bsf(movemask(valid &(a == vreduce_max(a)))); }
+
+////////////////////////////////////////////////////////////////////////////////
+/// Memory load and store operations
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline ssef load4f(const float4& a) {
+#ifdef __KERNEL_WITH_SSE_ALIGN__
+ return _mm_load_ps(&a.x);
+#else
+ return _mm_loadu_ps(&a.x);
+#endif
+}
+
+__forceinline ssef load4f(const float3& a) {
+#ifdef __KERNEL_WITH_SSE_ALIGN__
+ return _mm_load_ps(&a.x);
+#else
+ return _mm_loadu_ps(&a.x);
+#endif
+}
+
+__forceinline ssef load4f(const void* const a) {
+ return _mm_load_ps((float*)a);
+}
+
+__forceinline ssef load1f_first(const float a) {
+ return _mm_set_ss(a);
+}
+
+__forceinline void store4f(void* ptr, const ssef& v) {
+ _mm_store_ps((float*)ptr,v);
+}
+
+__forceinline ssef loadu4f(const void* const a) {
+ return _mm_loadu_ps((float*)a);
+}
+
+__forceinline void storeu4f(void* ptr, const ssef& v) {
+ _mm_storeu_ps((float*)ptr,v);
+}
+
+__forceinline void store4f(const sseb& mask, void* ptr, const ssef& f) {
+#if defined(__KERNEL_AVX__)
+ _mm_maskstore_ps((float*)ptr,(__m128i)mask,f);
+#else
+ *(ssef*)ptr = select(mask,f,*(ssef*)ptr);
+#endif
+}
+
+__forceinline ssef load4f_nt(void* ptr) {
+#if defined(__KERNEL_SSE41__)
+ return _mm_castsi128_ps(_mm_stream_load_si128((__m128i*)ptr));
+#else
+ return _mm_load_ps((float*)ptr);
+#endif
+}
+
+__forceinline void store4f_nt(void* ptr, const ssef& v) {
+#if defined(__KERNEL_SSE41__)
+ _mm_stream_ps((float*)ptr,v);
+#else
+ _mm_store_ps((float*)ptr,v);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Euclidian Space Operators
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline float dot(const ssef& a, const ssef& b) {
+ return reduce_add(a*b);
+}
+
+/* calculate shuffled cross product, useful when order of components does not matter */
+__forceinline ssef cross_zxy(const ssef& a, const ssef& b)
+{
+ const ssef a0 = a;
+ const ssef b0 = shuffle<1,2,0,3>(b);
+ const ssef a1 = shuffle<1,2,0,3>(a);
+ const ssef b1 = b;
+ return msub(a0,b0,a1*b1);
+}
+
+__forceinline ssef cross(const ssef& a, const ssef& b)
+{
+ return shuffle<1,2,0,3>(cross_zxy(a, b));
+}
+
+ccl_device_inline const ssef dot3_splat(const ssef& a, const ssef& b)
+{
+#ifdef __KERNEL_SSE41__
+ return _mm_dp_ps(a.m128, b.m128, 0x7f);
+#else
+ ssef t = a * b;
+ return ssef(((float*)&t)[0] + ((float*)&t)[1] + ((float*)&t)[2]);
+#endif
+}
+
+/* squared length taking only specified axes into account */
+template<size_t X, size_t Y, size_t Z, size_t W>
+ccl_device_inline float len_squared(const ssef& a)
+{
+#ifndef __KERNEL_SSE41__
+ float4& t = (float4 &)a;
+ return (X ? t.x * t.x : 0.0f) + (Y ? t.y * t.y : 0.0f) + (Z ? t.z * t.z : 0.0f) + (W ? t.w * t.w : 0.0f);
+#else
+ return extract<0>(ssef(_mm_dp_ps(a.m128, a.m128, (X << 4) | (Y << 5) | (Z << 6) | (W << 7) | 0xf)));
+#endif
+}
+
+ccl_device_inline float dot3(const ssef& a, const ssef& b)
+{
+#ifdef __KERNEL_SSE41__
+ return extract<0>(ssef(_mm_dp_ps(a.m128, b.m128, 0x7f)));
+#else
+ ssef t = a * b;
+ return ((float*)&t)[0] + ((float*)&t)[1] + ((float*)&t)[2];
+#endif
+}
+
+ccl_device_inline const ssef len3_squared_splat(const ssef& a)
+{
+ return dot3_splat(a, a);
+}
+
+ccl_device_inline float len3_squared(const ssef& a)
+{
+ return dot3(a, a);
+}
+
+ccl_device_inline float len3(const ssef& a)
+{
+ return extract<0>(mm_sqrt(dot3_splat(a, a)));
+}
+
+/* SSE shuffle utility functions */
+
+#ifdef __KERNEL_SSSE3__
+
+/* faster version for SSSE3 */
+typedef ssei shuffle_swap_t;
+
+ccl_device_inline const shuffle_swap_t shuffle_swap_identity(void)
+{
+ return _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
+}
+
+ccl_device_inline const shuffle_swap_t shuffle_swap_swap(void)
+{
+ return _mm_set_epi8(7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8);
+}
+
+ccl_device_inline const ssef shuffle_swap(const ssef& a, const shuffle_swap_t& shuf)
+{
+ return cast(_mm_shuffle_epi8(cast(a), shuf));
+}
+
+#else
+
+/* somewhat slower version for SSE2 */
+typedef int shuffle_swap_t;
+
+ccl_device_inline const shuffle_swap_t shuffle_swap_identity(void)
+{
+ return 0;
+}
+
+ccl_device_inline const shuffle_swap_t shuffle_swap_swap(void)
+{
+ return 1;
+}
+
+ccl_device_inline const ssef shuffle_swap(const ssef& a, shuffle_swap_t shuf)
+{
+ /* shuffle value must be a constant, so we need to branch */
+ if(shuf)
+ return ssef(_mm_shuffle_ps(a.m128, a.m128, _MM_SHUFFLE(1, 0, 3, 2)));
+ else
+ return ssef(_mm_shuffle_ps(a.m128, a.m128, _MM_SHUFFLE(3, 2, 1, 0)));
+}
+
+#endif
+
+#ifdef __KERNEL_SSE41__
+
+ccl_device_inline void gen_idirsplat_swap(const ssef &pn, const shuffle_swap_t &shuf_identity, const shuffle_swap_t &shuf_swap,
+ const float3& idir, ssef idirsplat[3], shuffle_swap_t shufflexyz[3])
+{
+ const __m128 idirsplat_raw[] = { _mm_set_ps1(idir.x), _mm_set_ps1(idir.y), _mm_set_ps1(idir.z) };
+ idirsplat[0] = _mm_xor_ps(idirsplat_raw[0], pn);
+ idirsplat[1] = _mm_xor_ps(idirsplat_raw[1], pn);
+ idirsplat[2] = _mm_xor_ps(idirsplat_raw[2], pn);
+
+ const ssef signmask = cast(ssei(0x80000000));
+ const ssef shuf_identity_f = cast(shuf_identity);
+ const ssef shuf_swap_f = cast(shuf_swap);
+
+ shufflexyz[0] = _mm_castps_si128(_mm_blendv_ps(shuf_identity_f, shuf_swap_f, _mm_and_ps(idirsplat_raw[0], signmask)));
+ shufflexyz[1] = _mm_castps_si128(_mm_blendv_ps(shuf_identity_f, shuf_swap_f, _mm_and_ps(idirsplat_raw[1], signmask)));
+ shufflexyz[2] = _mm_castps_si128(_mm_blendv_ps(shuf_identity_f, shuf_swap_f, _mm_and_ps(idirsplat_raw[2], signmask)));
+}
+
+#else
+
+ccl_device_inline void gen_idirsplat_swap(const ssef &pn, const shuffle_swap_t &shuf_identity, const shuffle_swap_t &shuf_swap,
+ const float3& idir, ssef idirsplat[3], shuffle_swap_t shufflexyz[3])
+{
+ idirsplat[0] = ssef(idir.x) ^ pn;
+ idirsplat[1] = ssef(idir.y) ^ pn;
+ idirsplat[2] = ssef(idir.z) ^ pn;
+
+ shufflexyz[0] = (idir.x >= 0)? shuf_identity: shuf_swap;
+ shufflexyz[1] = (idir.y >= 0)? shuf_identity: shuf_swap;
+ shufflexyz[2] = (idir.z >= 0)? shuf_identity: shuf_swap;
+}
+
+#endif
+
+ccl_device_inline const ssef uint32_to_float(const ssei &in)
+{
+ ssei a = _mm_srli_epi32(in, 16);
+ ssei b = _mm_and_si128(in, _mm_set1_epi32(0x0000ffff));
+ ssei c = _mm_or_si128(a, _mm_set1_epi32(0x53000000));
+ ssef d = _mm_cvtepi32_ps(b);
+ ssef e = _mm_sub_ps(_mm_castsi128_ps(c), _mm_castsi128_ps(_mm_set1_epi32(0x53000000)));
+ return _mm_add_ps(e, d);
+}
+
+template<size_t S1, size_t S2, size_t S3, size_t S4>
+ccl_device_inline const ssef set_sign_bit(const ssef &a)
+{
+ return a ^ cast(ssei(S1 << 31, S2 << 31, S3 << 31, S4 << 31));
+}
+
+#endif
+
+CCL_NAMESPACE_END
+
+#endif
+
diff --git a/intern/cycles/util/util_ssei.h b/intern/cycles/util/util_ssei.h
new file mode 100644
index 00000000000..5f5a8686e35
--- /dev/null
+++ b/intern/cycles/util/util_ssei.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2011-2013 Intel Corporation
+ * Modifications Copyright 2014, Blender Foundation.
+ *
+ * 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
+ */
+
+#ifndef __UTIL_SSEI_H__
+#define __UTIL_SSEI_H__
+
+CCL_NAMESPACE_BEGIN
+
+#ifdef __KERNEL_SSE2__
+
+/*! 4-wide SSE integer type. */
+struct ssei
+{
+ typedef sseb Mask; // mask type
+ typedef ssei Int; // int type
+ typedef ssef Float; // float type
+
+ enum { size = 4 }; // number of SIMD elements
+ union { __m128i m128; int32_t i[4]; }; // data
+
+ ////////////////////////////////////////////////////////////////////////////////
+ /// Constructors, Assignment & Cast Operators
+ ////////////////////////////////////////////////////////////////////////////////
+
+ __forceinline ssei ( ) {}
+ __forceinline ssei ( const ssei& a ) { m128 = a.m128; }
+ __forceinline ssei& operator=( const ssei& a ) { m128 = a.m128; return *this; }
+
+ __forceinline ssei( const __m128i a ) : m128(a) {}
+ __forceinline operator const __m128i&( void ) const { return m128; }
+ __forceinline operator __m128i&( void ) { return m128; }
+
+ __forceinline ssei ( const int a ) : m128(_mm_set1_epi32(a)) {}
+ __forceinline ssei ( int a, int b, int c, int d ) : m128(_mm_setr_epi32(a, b, c, d)) {}
+
+ __forceinline explicit ssei( const __m128 a ) : m128(_mm_cvtps_epi32(a)) {}
+
+ ////////////////////////////////////////////////////////////////////////////////
+ /// Array Access
+ ////////////////////////////////////////////////////////////////////////////////
+
+ __forceinline const int32_t& operator []( const size_t index ) const { assert(index < 4); return i[index]; }
+ __forceinline int32_t& operator []( const size_t index ) { assert(index < 4); return i[index]; }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+/// Unary Operators
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline const ssei cast ( const __m128& a ) { return _mm_castps_si128(a); }
+__forceinline const ssei operator +( const ssei& a ) { return a; }
+__forceinline const ssei operator -( const ssei& a ) { return _mm_sub_epi32(_mm_setzero_si128(), a.m128); }
+#if defined(__KERNEL_SSSE3__)
+__forceinline const ssei abs ( const ssei& a ) { return _mm_abs_epi32(a.m128); }
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+/// Binary Operators
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline const ssei operator +( const ssei& a, const ssei& b ) { return _mm_add_epi32(a.m128, b.m128); }
+__forceinline const ssei operator +( const ssei& a, const int32_t& b ) { return a + ssei(b); }
+__forceinline const ssei operator +( const int32_t& a, const ssei& b ) { return ssei(a) + b; }
+
+__forceinline const ssei operator -( const ssei& a, const ssei& b ) { return _mm_sub_epi32(a.m128, b.m128); }
+__forceinline const ssei operator -( const ssei& a, const int32_t& b ) { return a - ssei(b); }
+__forceinline const ssei operator -( const int32_t& a, const ssei& b ) { return ssei(a) - b; }
+
+#if defined(__KERNEL_SSE41__)
+__forceinline const ssei operator *( const ssei& a, const ssei& b ) { return _mm_mullo_epi32(a.m128, b.m128); }
+__forceinline const ssei operator *( const ssei& a, const int32_t& b ) { return a * ssei(b); }
+__forceinline const ssei operator *( const int32_t& a, const ssei& b ) { return ssei(a) * b; }
+#endif
+
+__forceinline const ssei operator &( const ssei& a, const ssei& b ) { return _mm_and_si128(a.m128, b.m128); }
+__forceinline const ssei operator &( const ssei& a, const int32_t& b ) { return a & ssei(b); }
+__forceinline const ssei operator &( const int32_t& a, const ssei& b ) { return ssei(a) & b; }
+
+__forceinline const ssei operator |( const ssei& a, const ssei& b ) { return _mm_or_si128(a.m128, b.m128); }
+__forceinline const ssei operator |( const ssei& a, const int32_t& b ) { return a | ssei(b); }
+__forceinline const ssei operator |( const int32_t& a, const ssei& b ) { return ssei(a) | b; }
+
+__forceinline const ssei operator ^( const ssei& a, const ssei& b ) { return _mm_xor_si128(a.m128, b.m128); }
+__forceinline const ssei operator ^( const ssei& a, const int32_t& b ) { return a ^ ssei(b); }
+__forceinline const ssei operator ^( const int32_t& a, const ssei& b ) { return ssei(a) ^ b; }
+
+__forceinline const ssei operator <<( const ssei& a, const int32_t& n ) { return _mm_slli_epi32(a.m128, n); }
+__forceinline const ssei operator >>( const ssei& a, const int32_t& n ) { return _mm_srai_epi32(a.m128, n); }
+
+__forceinline const ssei andnot(const ssei& a, const ssei& b) { return _mm_andnot_si128(a.m128,b.m128); }
+__forceinline const ssei andnot(const sseb& a, const ssei& b) { return _mm_andnot_si128(cast(a.m128),b.m128); }
+__forceinline const ssei andnot(const ssei& a, const sseb& b) { return _mm_andnot_si128(a.m128,cast(b.m128)); }
+
+__forceinline const ssei sra ( const ssei& a, const int32_t& b ) { return _mm_srai_epi32(a.m128, b); }
+__forceinline const ssei srl ( const ssei& a, const int32_t& b ) { return _mm_srli_epi32(a.m128, b); }
+
+#if defined(__KERNEL_SSE41__)
+__forceinline const ssei min( const ssei& a, const ssei& b ) { return _mm_min_epi32(a.m128, b.m128); }
+__forceinline const ssei min( const ssei& a, const int32_t& b ) { return min(a,ssei(b)); }
+__forceinline const ssei min( const int32_t& a, const ssei& b ) { return min(ssei(a),b); }
+
+__forceinline const ssei max( const ssei& a, const ssei& b ) { return _mm_max_epi32(a.m128, b.m128); }
+__forceinline const ssei max( const ssei& a, const int32_t& b ) { return max(a,ssei(b)); }
+__forceinline const ssei max( const int32_t& a, const ssei& b ) { return max(ssei(a),b); }
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+/// Assignment Operators
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline ssei& operator +=( ssei& a, const ssei& b ) { return a = a + b; }
+__forceinline ssei& operator +=( ssei& a, const int32_t& b ) { return a = a + b; }
+
+__forceinline ssei& operator -=( ssei& a, const ssei& b ) { return a = a - b; }
+__forceinline ssei& operator -=( ssei& a, const int32_t& b ) { return a = a - b; }
+
+#if defined(__KERNEL_SSE41__)
+__forceinline ssei& operator *=( ssei& a, const ssei& b ) { return a = a * b; }
+__forceinline ssei& operator *=( ssei& a, const int32_t& b ) { return a = a * b; }
+#endif
+
+__forceinline ssei& operator &=( ssei& a, const ssei& b ) { return a = a & b; }
+__forceinline ssei& operator &=( ssei& a, const int32_t& b ) { return a = a & b; }
+
+__forceinline ssei& operator |=( ssei& a, const ssei& b ) { return a = a | b; }
+__forceinline ssei& operator |=( ssei& a, const int32_t& b ) { return a = a | b; }
+
+__forceinline ssei& operator <<=( ssei& a, const int32_t& b ) { return a = a << b; }
+__forceinline ssei& operator >>=( ssei& a, const int32_t& b ) { return a = a >> b; }
+
+////////////////////////////////////////////////////////////////////////////////
+/// Comparison Operators + Select
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline const sseb operator ==( const ssei& a, const ssei& b ) { return _mm_castsi128_ps(_mm_cmpeq_epi32 (a.m128, b.m128)); }
+__forceinline const sseb operator ==( const ssei& a, const int32_t& b ) { return a == ssei(b); }
+__forceinline const sseb operator ==( const int32_t& a, const ssei& b ) { return ssei(a) == b; }
+
+__forceinline const sseb operator !=( const ssei& a, const ssei& b ) { return !(a == b); }
+__forceinline const sseb operator !=( const ssei& a, const int32_t& b ) { return a != ssei(b); }
+__forceinline const sseb operator !=( const int32_t& a, const ssei& b ) { return ssei(a) != b; }
+
+__forceinline const sseb operator < ( const ssei& a, const ssei& b ) { return _mm_castsi128_ps(_mm_cmplt_epi32 (a.m128, b.m128)); }
+__forceinline const sseb operator < ( const ssei& a, const int32_t& b ) { return a < ssei(b); }
+__forceinline const sseb operator < ( const int32_t& a, const ssei& b ) { return ssei(a) < b; }
+
+__forceinline const sseb operator >=( const ssei& a, const ssei& b ) { return !(a < b); }
+__forceinline const sseb operator >=( const ssei& a, const int32_t& b ) { return a >= ssei(b); }
+__forceinline const sseb operator >=( const int32_t& a, const ssei& b ) { return ssei(a) >= b; }
+
+__forceinline const sseb operator > ( const ssei& a, const ssei& b ) { return _mm_castsi128_ps(_mm_cmpgt_epi32 (a.m128, b.m128)); }
+__forceinline const sseb operator > ( const ssei& a, const int32_t& b ) { return a > ssei(b); }
+__forceinline const sseb operator > ( const int32_t& a, const ssei& b ) { return ssei(a) > b; }
+
+__forceinline const sseb operator <=( const ssei& a, const ssei& b ) { return !(a > b); }
+__forceinline const sseb operator <=( const ssei& a, const int32_t& b ) { return a <= ssei(b); }
+__forceinline const sseb operator <=( const int32_t& a, const ssei& b ) { return ssei(a) <= b; }
+
+__forceinline const ssei select( const sseb& m, const ssei& t, const ssei& f ) {
+#ifdef __KERNEL_SSE41__
+ return _mm_castps_si128(_mm_blendv_ps(_mm_castsi128_ps(f), _mm_castsi128_ps(t), m));
+#else
+ return _mm_or_si128(_mm_and_si128(m, t), _mm_andnot_si128(m, f));
+#endif
+}
+
+__forceinline const ssei select( const int mask, const ssei& t, const ssei& f ) {
+#if defined(__KERNEL_SSE41__) && ((!defined(__clang__) && !defined(_MSC_VER)) || defined(__INTEL_COMPILER))
+ return _mm_castps_si128(_mm_blend_ps(_mm_castsi128_ps(f), _mm_castsi128_ps(t), mask));
+#else
+ return select(sseb(mask),t,f);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Movement/Shifting/Shuffling Functions
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline ssei unpacklo( const ssei& a, const ssei& b ) { return _mm_castps_si128(_mm_unpacklo_ps(_mm_castsi128_ps(a.m128), _mm_castsi128_ps(b.m128))); }
+__forceinline ssei unpackhi( const ssei& a, const ssei& b ) { return _mm_castps_si128(_mm_unpackhi_ps(_mm_castsi128_ps(a.m128), _mm_castsi128_ps(b.m128))); }
+
+template<size_t i0, size_t i1, size_t i2, size_t i3> __forceinline const ssei shuffle( const ssei& a ) {
+ return _mm_shuffle_epi32(a, _MM_SHUFFLE(i3, i2, i1, i0));
+}
+
+template<size_t i0, size_t i1, size_t i2, size_t i3> __forceinline const ssei shuffle( const ssei& a, const ssei& b ) {
+ return _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(a), _mm_castsi128_ps(b), _MM_SHUFFLE(i3, i2, i1, i0)));
+}
+
+#if defined(__KERNEL_SSE3__)
+template<> __forceinline const ssei shuffle<0, 0, 2, 2>( const ssei& a ) { return _mm_castps_si128(_mm_moveldup_ps(_mm_castsi128_ps(a))); }
+template<> __forceinline const ssei shuffle<1, 1, 3, 3>( const ssei& a ) { return _mm_castps_si128(_mm_movehdup_ps(_mm_castsi128_ps(a))); }
+template<> __forceinline const ssei shuffle<0, 1, 0, 1>( const ssei& a ) { return _mm_castpd_si128(_mm_movedup_pd (_mm_castsi128_pd(a))); }
+#endif
+
+template<size_t i0> __forceinline const ssei shuffle( const ssei& b ) {
+ return shuffle<i0,i0,i0,i0>(b);
+}
+
+#if defined(__KERNEL_SSE41__)
+template<size_t src> __forceinline int extract( const ssei& b ) { return _mm_extract_epi32(b, src); }
+template<size_t dst> __forceinline const ssei insert( const ssei& a, const int32_t b ) { return _mm_insert_epi32(a, b, dst); }
+#else
+template<size_t src> __forceinline int extract( const ssei& b ) { return b[src]; }
+template<size_t dst> __forceinline const ssei insert( const ssei& a, const int32_t b ) { ssei c = a; c[dst] = b; return c; }
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+/// Reductions
+////////////////////////////////////////////////////////////////////////////////
+
+#if defined(__KERNEL_SSE41__)
+__forceinline const ssei vreduce_min(const ssei& v) { ssei h = min(shuffle<1,0,3,2>(v),v); return min(shuffle<2,3,0,1>(h),h); }
+__forceinline const ssei vreduce_max(const ssei& v) { ssei h = max(shuffle<1,0,3,2>(v),v); return max(shuffle<2,3,0,1>(h),h); }
+__forceinline const ssei vreduce_add(const ssei& v) { ssei h = shuffle<1,0,3,2>(v) + v ; return shuffle<2,3,0,1>(h) + h ; }
+
+__forceinline int reduce_min(const ssei& v) { return extract<0>(vreduce_min(v)); }
+__forceinline int reduce_max(const ssei& v) { return extract<0>(vreduce_max(v)); }
+__forceinline int reduce_add(const ssei& v) { return extract<0>(vreduce_add(v)); }
+
+__forceinline size_t select_min(const ssei& v) { return __bsf(movemask(v == vreduce_min(v))); }
+__forceinline size_t select_max(const ssei& v) { return __bsf(movemask(v == vreduce_max(v))); }
+
+__forceinline size_t select_min(const sseb& valid, const ssei& v) { const ssei a = select(valid,v,ssei((int)pos_inf)); return __bsf(movemask(valid & (a == vreduce_min(a)))); }
+__forceinline size_t select_max(const sseb& valid, const ssei& v) { const ssei a = select(valid,v,ssei((int)neg_inf)); return __bsf(movemask(valid & (a == vreduce_max(a)))); }
+
+#else
+
+__forceinline int reduce_min(const ssei& v) { return min(min(v[0],v[1]),min(v[2],v[3])); }
+__forceinline int reduce_max(const ssei& v) { return max(max(v[0],v[1]),max(v[2],v[3])); }
+__forceinline int reduce_add(const ssei& v) { return v[0]+v[1]+v[2]+v[3]; }
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+/// Memory load and store operations
+////////////////////////////////////////////////////////////////////////////////
+
+__forceinline ssei load4i( const void* const a ) {
+ return _mm_load_si128((__m128i*)a);
+}
+
+__forceinline void store4i(void* ptr, const ssei& v) {
+ _mm_store_si128((__m128i*)ptr,v);
+}
+
+__forceinline void storeu4i(void* ptr, const ssei& v) {
+ _mm_storeu_si128((__m128i*)ptr,v);
+}
+
+__forceinline void store4i( const sseb& mask, void* ptr, const ssei& i ) {
+#if defined (__KERNEL_AVX__)
+ _mm_maskstore_ps((float*)ptr,(__m128i)mask,_mm_castsi128_ps(i));
+#else
+ *(ssei*)ptr = select(mask,i,*(ssei*)ptr);
+#endif
+}
+
+__forceinline ssei load4i_nt (void* ptr) {
+#if defined(__KERNEL_SSE41__)
+ return _mm_stream_load_si128((__m128i*)ptr);
+#else
+ return _mm_load_si128((__m128i*)ptr);
+#endif
+}
+
+__forceinline void store4i_nt(void* ptr, const ssei& v) {
+#if defined(__KERNEL_SSE41__)
+ _mm_stream_ps((float*)ptr,_mm_castsi128_ps(v));
+#else
+ _mm_store_si128((__m128i*)ptr,v);
+#endif
+}
+
+#endif
+
+CCL_NAMESPACE_END
+
+#endif
+
diff --git a/intern/cycles/util/util_system.cpp b/intern/cycles/util/util_system.cpp
index 0764f7d9345..7c0445577e2 100644
--- a/intern/cycles/util/util_system.cpp
+++ b/intern/cycles/util/util_system.cpp
@@ -127,9 +127,12 @@ struct CPUCapabilities {
bool sse42;
bool sse4a;
bool avx;
+ bool avx2;
bool xop;
bool fma3;
bool fma4;
+ bool bmi1;
+ bool bmi2;
};
static CPUCapabilities& system_cpu_capabilities()
@@ -180,6 +183,11 @@ static CPUCapabilities& system_cpu_capabilities()
#endif
caps.avx = (xcr_feature_mask & 0x6) == 0x6;
}
+
+ __cpuid(result, 0x00000007);
+ caps.bmi1 = (result[1] & ((int)1 << 3)) != 0;
+ caps.bmi2 = (result[1] & ((int)1 << 8)) != 0;
+ caps.avx2 = (result[1] & ((int)1 << 5)) != 0;
}
#if 0
@@ -221,6 +229,11 @@ bool system_cpu_support_avx()
CPUCapabilities& caps = system_cpu_capabilities();
return caps.sse && caps.sse2 && caps.sse3 && caps.ssse3 && caps.sse41 && caps.avx;
}
+bool system_cpu_support_avx2()
+{
+ CPUCapabilities& caps = system_cpu_capabilities();
+ return caps.sse && caps.sse2 && caps.sse3 && caps.ssse3 && caps.sse41 && caps.avx && caps.avx2 && caps.fma3 && caps.bmi1 && caps.bmi2;
+}
#else
bool system_cpu_support_sse2()
@@ -242,6 +255,10 @@ bool system_cpu_support_avx()
{
return false;
}
+bool system_cpu_support_avx2()
+{
+ return false;
+}
#endif
diff --git a/intern/cycles/util/util_system.h b/intern/cycles/util/util_system.h
index 4409ea752cd..0e8868c7dfc 100644
--- a/intern/cycles/util/util_system.h
+++ b/intern/cycles/util/util_system.h
@@ -28,6 +28,7 @@ bool system_cpu_support_sse2();
bool system_cpu_support_sse3();
bool system_cpu_support_sse41();
bool system_cpu_support_avx();
+bool system_cpu_support_avx2();
CCL_NAMESPACE_END
diff --git a/intern/cycles/util/util_types.h b/intern/cycles/util/util_types.h
index b1319011936..98d70786d44 100644
--- a/intern/cycles/util/util_types.h
+++ b/intern/cycles/util/util_types.h
@@ -51,6 +51,7 @@
#endif
#define ccl_may_alias
#define ccl_always_inline __forceinline
+#define ccl_maybe_unused
#else
@@ -62,6 +63,7 @@
#define ccl_try_align(...) __attribute__((aligned(__VA_ARGS__)))
#define ccl_may_alias __attribute__((__may_alias__))
#define ccl_always_inline __attribute__((always_inline))
+#define ccl_maybe_unused __attribute__((used))
#endif
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index 0df149c44aa..7b041ea496f 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -407,7 +407,7 @@ extern GHOST_TSuccess GHOST_SetCursorPosition(GHOST_SystemHandle systemhandle,
*/
extern GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle,
GHOST_TGrabCursorMode mode,
- int bounds[4], int mouse_ungrab_xy[2]);
+ int bounds[4], const int mouse_ungrab_xy[2]);
/***************************************************************************************
* Access to mouse button and keyboard states.
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index f338d97ce16..f074e2b0ecc 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -363,7 +363,7 @@ GHOST_TSuccess GHOST_SetCursorPosition(GHOST_SystemHandle systemhandle,
GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle,
GHOST_TGrabCursorMode mode,
- int bounds[4], int mouse_ungrab_xy[2])
+ int bounds[4], const int mouse_ungrab_xy[2])
{
GHOST_IWindow *window = (GHOST_IWindow *) windowhandle;
GHOST_Rect bounds_rect, bounds_win;
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm
index 6a3971fa63a..2613f8a7f2d 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.mm
+++ b/intern/ghost/intern/GHOST_SystemCocoa.mm
@@ -1083,7 +1083,7 @@ GHOST_TUns8 GHOST_SystemCocoa::handleQuitRequest()
//Check open windows if some changes are not saved
if (m_windowManager->getAnyModifiedState())
{
- int shouldQuit = NSRunAlertPanel(@"Exit Blender", @"Some changes have not been saved.\nDo you really want to quit ?",
+ int shouldQuit = NSRunAlertPanel(@"Exit Blender", @"Some changes have not been saved.\nDo you really want to quit?",
@"Cancel", @"Quit Anyway", nil);
if (shouldQuit == NSAlertAlternateReturn)
{
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 960cbe1b8dc..ac0d5f3fadf 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -419,15 +419,8 @@ GHOST_TSuccess GHOST_SystemWin32::exit()
GHOST_TKey GHOST_SystemWin32::hardKey(GHOST_IWindow *window, RAWINPUT const& raw, int *keyDown, char *vk)
{
- GHOST_TKey key = GHOST_kKeyUnknown;
-
-
- if (!keyDown)
- return GHOST_kKeyUnknown;
-
-
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
-
+ GHOST_TKey key = GHOST_kKeyUnknown;
GHOST_ModifierKeys modifiers;
system->retrieveModifierKeys(modifiers);
@@ -1429,6 +1422,6 @@ int GHOST_SystemWin32::toggleConsole(int action)
int GHOST_SystemWin32::confirmQuit(GHOST_IWindow *window) const
{
- return (MessageBox(window ? ((GHOST_WindowWin32 *)window)->getHWND() : 0, "Some changes have not been saved.\nDo you really want to quit ?",
+ return (MessageBox(window ? ((GHOST_WindowWin32 *)window)->getHWND() : 0, "Some changes have not been saved.\nDo you really want to quit?",
"Exit Blender", MB_OKCANCEL | MB_ICONWARNING | MB_TOPMOST) == IDOK);
}
diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm
index 5a86c6fe59d..35e7e22b5ce 100644
--- a/intern/ghost/intern/GHOST_WindowCocoa.mm
+++ b/intern/ghost/intern/GHOST_WindowCocoa.mm
@@ -643,7 +643,7 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(
char darwin_ver[10];
size_t len = sizeof(darwin_ver);
sysctlbyname("kern.osrelease", &darwin_ver, &len, NULL, 0);
- if(darwin_ver[0] == '1' && darwin_ver[1] <= '3') {
+ if(darwin_ver[0] == '1' && darwin_ver[1] >= '3') {
m_lionStyleFullScreen = true;
}
diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h
index 17e6b6ca079..176fdd8d188 100644
--- a/intern/guardedalloc/MEM_guardedalloc.h
+++ b/intern/guardedalloc/MEM_guardedalloc.h
@@ -128,6 +128,12 @@ extern "C" {
extern void *(*MEM_mallocN)(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
/**
+ * Allocate an aligned block of memory of size len, with tag name str. The
+ * name must be a static, because only a pointer to it is stored !
+ * */
+ extern void *(*MEM_mallocN_aligned)(size_t len, size_t alignment, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(3);
+
+ /**
* Same as callocN, clears memory and uses mmap (disk cached) if supported.
* Can be free'd with MEM_freeN as usual.
* */
diff --git a/intern/guardedalloc/intern/mallocn.c b/intern/guardedalloc/intern/mallocn.c
index e85fba7a6d0..40f406d0ae2 100644
--- a/intern/guardedalloc/intern/mallocn.c
+++ b/intern/guardedalloc/intern/mallocn.c
@@ -32,6 +32,8 @@
/* to ensure strict conversions */
#include "../../source/blender/blenlib/BLI_strict_flags.h"
+#include <assert.h>
+
#include "mallocn_intern.h"
size_t (*MEM_allocN_len)(const void *vmemh) = MEM_lockfree_allocN_len;
@@ -41,6 +43,7 @@ void *(*MEM_reallocN_id)(void *vmemh, size_t len, const char *str) = MEM_lockfre
void *(*MEM_recallocN_id)(void *vmemh, size_t len, const char *str) = MEM_lockfree_recallocN_id;
void *(*MEM_callocN)(size_t len, const char *str) = MEM_lockfree_callocN;
void *(*MEM_mallocN)(size_t len, const char *str) = MEM_lockfree_mallocN;
+void *(*MEM_mallocN_aligned)(size_t len, size_t alignment, const char *str) = MEM_lockfree_mallocN_aligned;
void *(*MEM_mapallocN)(size_t len, const char *str) = MEM_lockfree_mapallocN;
void (*MEM_printmemlist_pydict)(void) = MEM_lockfree_printmemlist_pydict;
void (*MEM_printmemlist)(void) = MEM_lockfree_printmemlist;
@@ -60,6 +63,40 @@ uintptr_t (*MEM_get_peak_memory)(void) = MEM_lockfree_get_peak_memory;
const char *(*MEM_name_ptr)(void *vmemh) = MEM_lockfree_name_ptr;
#endif
+void *aligned_malloc(size_t size, size_t alignment)
+{
+#ifdef _WIN32
+ return _aligned_malloc(size, alignment);
+#elif defined(__APPLE__)
+ /* On Mac OS X, both the heap and the stack are guaranteed 16-byte aligned so
+ * they work natively with SSE types with no further work.
+ */
+ assert(alignment == 16);
+ return malloc(size);
+#elif defined(__FreeBSD__) || defined(__NetBSD__)
+ void *result;
+
+ if (posix_memalign(&result, alignment, size)) {
+ /* non-zero means allocation error
+ * either no allocation or bad alignment value
+ */
+ return NULL;
+ }
+ return result;
+#else /* This is for Linux. */
+ return memalign(alignment, size);
+#endif
+}
+
+void aligned_free(void *ptr)
+{
+#ifdef _WIN32
+ _aligned_free(ptr);
+#else
+ free(ptr);
+#endif
+}
+
void MEM_use_guarded_allocator(void)
{
MEM_allocN_len = MEM_guarded_allocN_len;
@@ -69,6 +106,7 @@ void MEM_use_guarded_allocator(void)
MEM_recallocN_id = MEM_guarded_recallocN_id;
MEM_callocN = MEM_guarded_callocN;
MEM_mallocN = MEM_guarded_mallocN;
+ MEM_mallocN_aligned = MEM_guarded_mallocN_aligned;
MEM_mapallocN = MEM_guarded_mapallocN;
MEM_printmemlist_pydict = MEM_guarded_printmemlist_pydict;
MEM_printmemlist = MEM_guarded_printmemlist;
diff --git a/intern/guardedalloc/intern/mallocn_guarded_impl.c b/intern/guardedalloc/intern/mallocn_guarded_impl.c
index 172c79d50cd..6e32844e2e7 100644
--- a/intern/guardedalloc/intern/mallocn_guarded_impl.c
+++ b/intern/guardedalloc/intern/mallocn_guarded_impl.c
@@ -113,7 +113,10 @@ typedef struct MemHead {
const char *name;
const char *nextname;
int tag2;
- int mmap; /* if true, memory was mmapped */
+ short mmap; /* if true, memory was mmapped */
+ short alignment; /* if non-zero aligned alloc was used
+ * and alignment is stored here.
+ */
#ifdef DEBUG_MEMCOUNTER
int _count;
#endif
@@ -128,6 +131,8 @@ typedef struct MemHead {
#endif
} MemHead;
+typedef MemHead MemHeadAligned;
+
/* for openmp threading asserts, saves time troubleshooting
* we may need to extend this if blender code starts using MEM_
* functions inside OpenMP correctly with omp_set_lock() */
@@ -187,7 +192,7 @@ static const char *check_memlist(MemHead *memh);
#define MEMNEXT(x) \
((MemHead *)(((char *) x) - ((char *) &(((MemHead *)0)->next))))
-
+
/* --------------------------------------------------------------------- */
/* vars */
/* --------------------------------------------------------------------- */
@@ -325,10 +330,12 @@ void *MEM_guarded_dupallocN(const void *vmemh)
memh--;
#ifndef DEBUG_MEMDUPLINAME
- if (memh->mmap)
+ if (UNLIKELY(memh->mmap))
+ newp = MEM_guarded_mapallocN(memh->len, "dupli_mapalloc");
+ else if (LIKELY(memh->alignment == 0))
newp = MEM_guarded_mapallocN(memh->len, "dupli_mapalloc");
else
- newp = MEM_guarded_mallocN(memh->len, "dupli_alloc");
+ newp = MEM_guarded_mallocN_aligned(memh->len, (size_t) memh->alignment, "dupli_alloc");
if (newp == NULL) return NULL;
#else
@@ -336,14 +343,18 @@ void *MEM_guarded_dupallocN(const void *vmemh)
MemHead *nmemh;
char *name = malloc(strlen(memh->name) + 24);
- if (memh->mmap) {
+ if (UNLIKELY(memh->mmap)) {
sprintf(name, "%s %s", "dupli_mapalloc", memh->name);
newp = MEM_guarded_mapallocN(memh->len, name);
}
- else {
+ else if (LIKELY(memh->alignment == 0)) {
sprintf(name, "%s %s", "dupli_alloc", memh->name);
newp = MEM_guarded_mallocN(memh->len, name);
}
+ else {
+ sprintf(name, "%s %s", "dupli_alloc", memh->name);
+ newp = MEM_guarded_mallocN_aligned(memh->len, (size_t) memh->alignment, name);
+ }
if (newp == NULL) return NULL;
@@ -368,7 +379,13 @@ void *MEM_guarded_reallocN_id(void *vmemh, size_t len, const char *str)
MemHead *memh = vmemh;
memh--;
- newp = MEM_guarded_mallocN(len, memh->name);
+ if (LIKELY(memh->alignment == 0)) {
+ newp = MEM_guarded_mallocN(len, memh->name);
+ }
+ else {
+ newp = MEM_guarded_mallocN_aligned(len, (size_t) memh->alignment, memh->name);
+ }
+
if (newp) {
if (len < memh->len) {
/* shrink */
@@ -397,7 +414,13 @@ void *MEM_guarded_recallocN_id(void *vmemh, size_t len, const char *str)
MemHead *memh = vmemh;
memh--;
- newp = MEM_guarded_mallocN(len, memh->name);
+ if (LIKELY(memh->alignment == 0)) {
+ newp = MEM_guarded_mallocN(len, memh->name);
+ }
+ else {
+ newp = MEM_guarded_mallocN_aligned(len, (size_t) memh->alignment, memh->name);
+ }
+
if (newp) {
if (len < memh->len) {
/* shrink */
@@ -464,6 +487,7 @@ static void make_memhead_header(MemHead *memh, size_t len, const char *str)
memh->nextname = NULL;
memh->len = len;
memh->mmap = 0;
+ memh->alignment = 0;
memh->tag2 = MEMTAG2;
#ifdef DEBUG_MEMDUPLINAME
@@ -514,6 +538,54 @@ void *MEM_guarded_mallocN(size_t len, const char *str)
return NULL;
}
+void *MEM_guarded_mallocN_aligned(size_t len, size_t alignment, const char *str)
+{
+ MemHead *memh;
+
+ /* It's possible that MemHead's size is not properly aligned,
+ * do extra padding to deal with this.
+ *
+ * We only support small alignments which fits into short in
+ * order to save some bits in MemHead structure.
+ */
+ size_t extra_padding = MEMHEAD_ALIGN_PADDING(alignment);
+
+ /* Huge alignment values doesn't make sense and they
+ * wouldn't fit into 'short' used in the MemHead.
+ */
+ assert(alignment < 1024);
+
+ /* We only support alignment to a power of two. */
+ assert(IS_POW2(alignment));
+
+ len = SIZET_ALIGN_4(len);
+
+ memh = (MemHead *)aligned_malloc(len + extra_padding + sizeof(MemHead) + sizeof(MemTail), alignment);
+
+ if (LIKELY(memh)) {
+ /* We keep padding in the beginning of MemHead,
+ * this way it's always possible to get MemHead
+ * from the data pointer.
+ */
+ memh = (MemHead *)((char *)memh + extra_padding);
+
+ make_memhead_header(memh, len, str);
+ memh->alignment = (short) alignment;
+ if (UNLIKELY(malloc_debug_memset && len))
+ memset(memh + 1, 255, len);
+
+#ifdef DEBUG_MEMCOUNTER
+ if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
+ memcount_raise(__func__);
+ memh->_count = _mallocn_count++;
+#endif
+ return (++memh);
+ }
+ print_error("aligned_malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
+ SIZET_ARG(len), str, (unsigned int) mem_in_use);
+ return NULL;
+}
+
void *MEM_guarded_callocN(size_t len, const char *str)
{
MemHead *memh;
@@ -953,7 +1025,12 @@ static void rem_memblock(MemHead *memh)
else {
if (UNLIKELY(malloc_debug_memset && memh->len))
memset(memh + 1, 255, memh->len);
- free(memh);
+ if (LIKELY(memh->alignment == 0)) {
+ free(memh);
+ }
+ else {
+ aligned_free(MEMHEAD_REAL_PTR(memh));
+ }
}
}
diff --git a/intern/guardedalloc/intern/mallocn_intern.h b/intern/guardedalloc/intern/mallocn_intern.h
index 7c8922dd407..523d1786b97 100644
--- a/intern/guardedalloc/intern/mallocn_intern.h
+++ b/intern/guardedalloc/intern/mallocn_intern.h
@@ -85,6 +85,35 @@
# define UNLIKELY(x) (x)
#endif
+#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__)
+// Needed for memalign on Linux and _aligned_alloc on Windows.
+# ifdef FREE_WINDOWS
+/* make sure _aligned_malloc is included */
+# ifdef __MSVCRT_VERSION__
+# undef __MSVCRT_VERSION__
+# endif
+
+# define __MSVCRT_VERSION__ 0x0700
+# endif // FREE_WINDOWS
+
+# include <malloc.h>
+#else
+// Apple's malloc is 16-byte aligned, and does not have malloc.h, so include
+// stdilb instead.
+# include <stdlib.h>
+#endif
+
+#define IS_POW2(a) (((a) & ((a) - 1)) == 0)
+
+/* Extra padding which needs to be applied on MemHead to make it aligned. */
+#define MEMHEAD_ALIGN_PADDING(alignment) ((size_t)alignment - (sizeof(MemHeadAligned) % (size_t)alignment))
+
+/* Real pointer returned by the malloc or aligned_alloc. */
+#define MEMHEAD_REAL_PTR(memh) ((char *)memh - MEMHEAD_ALIGN_PADDING(memh->alignment))
+
+void *aligned_malloc(size_t size, size_t alignment);
+void aligned_free(void *ptr);
+
/* Prototypes for counted allocator functions */
size_t MEM_lockfree_allocN_len(const void *vmemh) ATTR_WARN_UNUSED_RESULT;
void MEM_lockfree_freeN(void *vmemh);
@@ -93,6 +122,7 @@ void *MEM_lockfree_reallocN_id(void *vmemh, size_t len, const char *UNUSED(str))
void *MEM_lockfree_recallocN_id(void *vmemh, size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(2);
void *MEM_lockfree_callocN(size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
void *MEM_lockfree_mallocN(size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
+void *MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(3);
void *MEM_lockfree_mapallocN(size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
void MEM_lockfree_printmemlist_pydict(void);
void MEM_lockfree_printmemlist(void);
@@ -119,6 +149,7 @@ void *MEM_guarded_reallocN_id(void *vmemh, size_t len, const char *UNUSED(str))
void *MEM_guarded_recallocN_id(void *vmemh, size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(2);
void *MEM_guarded_callocN(size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
void *MEM_guarded_mallocN(size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
+void *MEM_guarded_mallocN_aligned(size_t len, size_t alignment, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(3);
void *MEM_guarded_mapallocN(size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
void MEM_guarded_printmemlist_pydict(void);
void MEM_guarded_printmemlist(void);
diff --git a/intern/guardedalloc/intern/mallocn_lockfree_impl.c b/intern/guardedalloc/intern/mallocn_lockfree_impl.c
index 6fc01807af3..ffb23702601 100644
--- a/intern/guardedalloc/intern/mallocn_lockfree_impl.c
+++ b/intern/guardedalloc/intern/mallocn_lockfree_impl.c
@@ -46,6 +46,11 @@ typedef struct MemHead {
size_t len;
} MemHead;
+typedef struct MemHeadAligned {
+ short alignment;
+ size_t len;
+} MemHeadAligned;
+
static unsigned int totblock = 0;
static size_t mem_in_use = 0, mmap_in_use = 0, peak_mem = 0;
static bool malloc_debug_memset = false;
@@ -54,9 +59,16 @@ static void (*error_callback)(const char *) = NULL;
static void (*thread_lock_callback)(void) = NULL;
static void (*thread_unlock_callback)(void) = NULL;
+enum {
+ MEMHEAD_MMAP_FLAG = 1,
+ MEMHEAD_ALIGN_FLAG = 2,
+};
+
#define MEMHEAD_FROM_PTR(ptr) (((MemHead*) vmemh) - 1)
#define PTR_FROM_MEMHEAD(memhead) (memhead + 1)
-#define MEMHEAD_IS_MMAP(memhead) ((memhead)->len & (size_t) 1)
+#define MEMHEAD_ALIGNED_FROM_PTR(ptr) (((MemHeadAligned*) vmemh) - 1)
+#define MEMHEAD_IS_MMAP(memhead) ((memhead)->len & (size_t) MEMHEAD_MMAP_FLAG)
+#define MEMHEAD_IS_ALIGNED(memhead) ((memhead)->len & (size_t) MEMHEAD_ALIGN_FLAG)
#ifdef __GNUC__
__attribute__ ((format(printf, 1, 2)))
@@ -93,7 +105,7 @@ static void mem_unlock_thread(void)
size_t MEM_lockfree_allocN_len(const void *vmemh)
{
if (vmemh) {
- return MEMHEAD_FROM_PTR(vmemh)->len & ~((size_t) 1);
+ return MEMHEAD_FROM_PTR(vmemh)->len & ~((size_t) (MEMHEAD_MMAP_FLAG | MEMHEAD_ALIGN_FLAG));
}
else {
return 0;
@@ -124,7 +136,13 @@ void MEM_lockfree_freeN(void *vmemh)
if (UNLIKELY(malloc_debug_memset && len)) {
memset(memh + 1, 255, len);
}
- free(memh);
+ if (UNLIKELY(MEMHEAD_IS_ALIGNED(memh))) {
+ MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
+ aligned_free(MEMHEAD_REAL_PTR(memh_aligned));
+ }
+ else {
+ free(memh);
+ }
}
}
@@ -134,9 +152,16 @@ void *MEM_lockfree_dupallocN(const void *vmemh)
if (vmemh) {
MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
const size_t prev_size = MEM_allocN_len(vmemh);
- if (MEMHEAD_IS_MMAP(memh)) {
+ if (UNLIKELY(MEMHEAD_IS_MMAP(memh))) {
newp = MEM_lockfree_mapallocN(prev_size, "dupli_mapalloc");
}
+ else if (UNLIKELY(MEMHEAD_IS_ALIGNED(memh))) {
+ MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
+ newp = MEM_lockfree_mallocN_aligned(
+ prev_size,
+ (size_t)memh_aligned->alignment,
+ "dupli_malloc");
+ }
else {
newp = MEM_lockfree_mallocN(prev_size, "dupli_malloc");
}
@@ -150,9 +175,20 @@ void *MEM_lockfree_reallocN_id(void *vmemh, size_t len, const char *str)
void *newp = NULL;
if (vmemh) {
+ MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
size_t old_len = MEM_allocN_len(vmemh);
- newp = MEM_lockfree_mallocN(len, "realloc");
+ if (LIKELY(!MEMHEAD_IS_ALIGNED(memh))) {
+ newp = MEM_lockfree_mallocN(len, "realloc");
+ }
+ else {
+ MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
+ newp = MEM_lockfree_mallocN_aligned(
+ old_len,
+ (size_t)memh_aligned->alignment,
+ "realloc");
+ }
+
if (newp) {
if (len < old_len) {
/* shrink */
@@ -178,9 +214,19 @@ void *MEM_lockfree_recallocN_id(void *vmemh, size_t len, const char *str)
void *newp = NULL;
if (vmemh) {
+ MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
size_t old_len = MEM_allocN_len(vmemh);
- newp = MEM_lockfree_mallocN(len, "recalloc");
+ if (LIKELY(!MEMHEAD_IS_ALIGNED(memh))) {
+ newp = MEM_lockfree_mallocN(len, "recalloc");
+ }
+ else {
+ MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
+ newp = MEM_lockfree_mallocN_aligned(old_len,
+ (size_t)memh_aligned->alignment,
+ "recalloc");
+ }
+
if (newp) {
if (len < old_len) {
/* shrink */
@@ -256,6 +302,57 @@ void *MEM_lockfree_mallocN(size_t len, const char *str)
return NULL;
}
+void *MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *str)
+{
+ MemHeadAligned *memh;
+
+ /* It's possible that MemHead's size is not properly aligned,
+ * do extra padding to deal with this.
+ *
+ * We only support small alignments which fits into short in
+ * order to save some bits in MemHead structure.
+ */
+ size_t extra_padding = MEMHEAD_ALIGN_PADDING(alignment);
+
+ /* Huge alignment values doesn't make sense and they
+ * wouldn't fit into 'short' used in the MemHead.
+ */
+ assert(alignment < 1024);
+
+ /* We only support alignment to a power of two. */
+ assert(IS_POW2(alignment));
+
+ len = SIZET_ALIGN_4(len);
+
+ memh = (MemHeadAligned *)aligned_malloc(
+ len + extra_padding + sizeof(MemHeadAligned), alignment);
+
+ if (LIKELY(memh)) {
+ /* We keep padding in the beginning of MemHead,
+ * this way it's always possible to get MemHead
+ * from the data pointer.
+ */
+ memh = (MemHeadAligned *)((char *)memh + extra_padding);
+
+ if (UNLIKELY(malloc_debug_memset && len)) {
+ memset(memh + 1, 255, len);
+ }
+
+ memh->len = len | (size_t) MEMHEAD_ALIGN_FLAG;
+ memh->alignment = (short) alignment;
+ atomic_add_u(&totblock, 1);
+ atomic_add_z(&mem_in_use, len);
+
+ /* TODO(sergey): Not strictly speaking thread-safe. */
+ peak_mem = mem_in_use > peak_mem ? mem_in_use : peak_mem;
+
+ return PTR_FROM_MEMHEAD(memh);
+ }
+ print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
+ SIZET_ARG(len), str, (unsigned int) mem_in_use);
+ return NULL;
+}
+
void *MEM_lockfree_mapallocN(size_t len, const char *str)
{
MemHead *memh;
@@ -279,7 +376,7 @@ void *MEM_lockfree_mapallocN(size_t len, const char *str)
#endif
if (memh != (MemHead *)-1) {
- memh->len = len | (size_t) 1;
+ memh->len = len | (size_t) MEMHEAD_MMAP_FLAG;
atomic_add_u(&totblock, 1);
atomic_add_z(&mem_in_use, len);
atomic_add_z(&mmap_in_use, len);
diff --git a/release/darwin/codesigning_rules_blender.plist b/release/darwin/codesigning_rules_blender.plist
index aa5580dd322..5e6198e8ce7 100644
--- a/release/darwin/codesigning_rules_blender.plist
+++ b/release/darwin/codesigning_rules_blender.plist
@@ -5,7 +5,7 @@
<key>rules</key>
<dict>
<!-- Exclude datafiles, python and scripts -->
- <key>^MacOS/2.70</key>
+ <key>^MacOS/2.71</key>
<false/>
<key>^Resources/</key>
<true/>
diff --git a/release/darwin/codesigning_rules_player.plist b/release/darwin/codesigning_rules_player.plist
index ff154df7b49..acbdab20db9 100644
--- a/release/darwin/codesigning_rules_player.plist
+++ b/release/darwin/codesigning_rules_player.plist
@@ -5,7 +5,7 @@
<key>rules</key>
<dict>
<!-- Exclude datafiles, python and scripts -->
- <key>^MacOS/2.70</key>
+ <key>^MacOS/2.71</key>
<false/>
<!-- Exclude Resources for placing game.blend and own icons -->
<key>^Resources/</key>
diff --git a/release/datafiles/brushicons/texfill.png b/release/datafiles/brushicons/texfill.png
new file mode 100644
index 00000000000..85a83e0ea4e
--- /dev/null
+++ b/release/datafiles/brushicons/texfill.png
Binary files differ
diff --git a/release/datafiles/brushicons/texmask.png b/release/datafiles/brushicons/texmask.png
new file mode 100644
index 00000000000..7ab8dde3e3b
--- /dev/null
+++ b/release/datafiles/brushicons/texmask.png
Binary files differ
diff --git a/release/datafiles/locale b/release/datafiles/locale
-Subproject cb1967cc63a6d2d75d2b59cdf91c5f5645285ae
+Subproject 5ea1456cf5bf9caa32d4988284cc0b75637dd45
diff --git a/release/datafiles/splash.png b/release/datafiles/splash.png
index b496bb77245..451273728f2 100644
--- a/release/datafiles/splash.png
+++ b/release/datafiles/splash.png
Binary files differ
diff --git a/release/datafiles/splash_2x.png b/release/datafiles/splash_2x.png
index 654d07b093f..758178be298 100644
--- a/release/datafiles/splash_2x.png
+++ b/release/datafiles/splash_2x.png
Binary files differ
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject c50944e808d6c74148237e85866e893628f0fee
+Subproject 9948cd6025f29fad12eaf6d3b0e738dfb2b50d4
diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib
-Subproject 31545d25c9cb41d271a3f3ef84d327708572290
+Subproject 0bd307ae8cc7746b433c0baf9be13dc662b47f0
diff --git a/release/scripts/freestyle/modules/freestyle/chainingiterators.py b/release/scripts/freestyle/modules/freestyle/chainingiterators.py
index ae0a8a04294..5cd0dfeff59 100644
--- a/release/scripts/freestyle/modules/freestyle/chainingiterators.py
+++ b/release/scripts/freestyle/modules/freestyle/chainingiterators.py
@@ -23,6 +23,22 @@ rules. Also intended to be a collection of examples for defining
chaining iterators in Python
"""
+__all__ = (
+ "ChainPredicateIterator",
+ "ChainSilhouetteIterator",
+ "pyChainSilhouetteIterator",
+ "pyChainSilhouetteGenericIterator",
+ "pyExternalContourChainingIterator",
+ "pySketchyChainSilhouetteIterator",
+ "pySketchyChainingIterator",
+ "pyFillOcclusionsRelativeChainingIterator",
+ "pyFillOcclusionsAbsoluteChainingIterator",
+ "pyFillOcclusionsAbsoluteAndRelativeChainingIterator",
+ "pyFillQi0AbsoluteAndRelativeChainingIterator",
+ "pyNoIdChainSilhouetteIterator",
+ )
+
+
# module members
from _freestyle import (
ChainPredicateIterator,
@@ -41,11 +57,30 @@ from freestyle.predicates import (
)
from freestyle.utils import (
ContextFunctions as CF,
- stroke_normal,
+ get_chain_length,
+ find_matching_vertex,
)
+
import bpy
+NATURES = (
+ Nature.SILHOUETTE,
+ Nature.BORDER,
+ Nature.CREASE,
+ Nature.MATERIAL_BOUNDARY,
+ Nature.EDGE_MARK,
+ Nature.SUGGESTIVE_CONTOUR,
+ Nature.VALLEY,
+ Nature.RIDGE
+ )
+
+
+def nature_in_preceding(nature, index):
+ """ Returns True if given nature appears before index, else False """
+ return any(nature & nat for nat in NATURES[:index])
+
+
class pyChainSilhouetteIterator(ChainingIterator):
"""Natural chaining iterator
@@ -61,43 +96,28 @@ class pyChainSilhouetteIterator(ChainingIterator):
pass
def traverse(self, iter):
- winner = None
it = AdjacencyIterator(iter)
- tvertex = self.next_vertex
- if type(tvertex) is TVertex:
- mateVE = tvertex.get_mate(self.current_edge)
- while not it.is_end:
- ve = it.object
- if ve.id == mateVE.id:
- winner = ve
- break
- it.increment()
- else:
- ## case of NonTVertex
- natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK,
- Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
- for i in range(len(natures)):
- currentNature = self.current_edge.nature
- if (natures[i] & currentNature) != 0:
- count=0
- while not it.is_end:
- visitNext = 0
- oNature = it.object.nature
- if (oNature & natures[i]) != 0:
- if natures[i] != oNature:
- for j in range(i):
- if (natures[j] & oNature) != 0:
- visitNext = 1
- break
- if visitNext != 0:
- break
- count = count+1
- winner = it.object
- it.increment()
- if count != 1:
- winner = None
- break
- return winner
+ ## case of TVertex
+ vertex = self.next_vertex
+ if type(vertex) is TVertex:
+ mate = vertex.get_mate(self.current_edge)
+ return find_matching_vertex(mate.id, it)
+ ## case of NonTVertex
+ winner = None
+ for i, nat in enumerate(NATURES):
+ if (nat & self.current_edge.nature):
+ for ve in it:
+ ve_nat = ve.nature
+ if (ve_nat & nat):
+ # search for matches in previous natures. if match -> break
+ if nat != ve_nat and nature_in_preceding(ve_nat, index=i):
+ break
+ # a second match must be an error
+ if winner is not None:
+ return None
+ # assign winner
+ winner = ve
+ return winner
class pyChainSilhouetteGenericIterator(ChainingIterator):
@@ -120,47 +140,30 @@ class pyChainSilhouetteGenericIterator(ChainingIterator):
pass
def traverse(self, iter):
- winner = None
it = AdjacencyIterator(iter)
- tvertex = self.next_vertex
- if type(tvertex) is TVertex:
- mateVE = tvertex.get_mate(self.current_edge)
- while not it.is_end:
- ve = it.object
- if ve.id == mateVE.id:
- winner = ve
- break
- it.increment()
- else:
- ## case of NonTVertex
- natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK,
- Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
- for i in range(len(natures)):
- currentNature = self.current_edge.nature
- if (natures[i] & currentNature) != 0:
- count=0
- while not it.is_end:
- visitNext = 0
- oNature = it.object.nature
- ve = it.object
- if ve.id == self.current_edge.id:
- it.increment()
- continue
- if (oNature & natures[i]) != 0:
- if natures[i] != oNature:
- for j in range(i):
- if (natures[j] & oNature) != 0:
- visitNext = 1
- break
- if visitNext != 0:
- break
- count = count+1
- winner = ve
- it.increment()
- if count != 1:
- winner = None
- break
- return winner
+ ## case of TVertex
+ vertex = self.next_vertex
+ if type(vertex) is TVertex:
+ mate = vertex.get_mate(self.current_edge)
+ return find_matching_vertex(mate.id, it)
+ ## case of NonTVertex
+ winner = None
+ for i, nat in enumerate(NATURES):
+ if (nat & self.current_edge.nature):
+ for ve in it:
+ ve_nat = ve.nature
+ if ve.id == self.current_edge.id:
+ continue
+ if (ve_nat & nat):
+ if nat != ve_nat and nature_in_preceding(ve_nat, index=i):
+ break
+
+ if winner is not None:
+ return None
+
+ winner = ve
+ return winner
+ return None
class pyExternalContourChainingIterator(ChainingIterator):
@@ -168,49 +171,40 @@ class pyExternalContourChainingIterator(ChainingIterator):
def __init__(self):
ChainingIterator.__init__(self, False, True, None, True)
- self._isExternalContour = ExternalContourUP1D()
+ self.ExternalContour = ExternalContourUP1D()
def init(self):
self._nEdges = 0
- self._isInSelection = 1
def checkViewEdge(self, ve, orientation):
- if orientation != 0:
- vertex = ve.second_svertex()
- else:
- vertex = ve.first_svertex()
- it = AdjacencyIterator(vertex,1,1)
- while not it.is_end:
- ave = it.object
- if self._isExternalContour(ave):
- return True
- it.increment()
- if bpy.app.debug_freestyle:
+ vertex = (ve.first_viewvertex if orientation else
+ ve.last_viewvertex)
+
+ it = AdjacencyIterator(vertex, True, True)
+ result = any(self.ExternalContour(ave) for ave in it)
+ # report if there is no result (that's bad)
+ if not result and bpy.app.debug_freestyle:
print("pyExternalContourChainingIterator : didn't find next edge")
- return False
+
+ return result
def traverse(self, iter):
winner = None
+ self._nEdges += 1
+
it = AdjacencyIterator(iter)
- while not it.is_end:
- ve = it.object
- if self._isExternalContour(ve):
- if ve.time_stamp == CF.get_time_stamp():
- winner = ve
- it.increment()
+ time_stamp = CF.get_time_stamp()
+
+ for ve in it:
+ if self.ExternalContour(ve) and ve.time_stamp == time_stamp:
+ winner = ve
- self._nEdges = self._nEdges+1
if winner is None:
- orient = 1
it = AdjacencyIterator(iter)
- while not it.is_end:
- ve = it.object
- if it.is_incoming:
- orient = 0
- good = self.checkViewEdge(ve,orient)
- if good != 0:
+ for ve in it:
+ if self.checkViewEdge(ve, not it.is_incoming):
winner = ve
- it.increment()
+
return winner
@@ -227,58 +221,49 @@ class pySketchyChainSilhouetteIterator(ChainingIterator):
def __init__(self, nRounds=3,stayInSelection=True):
ChainingIterator.__init__(self, stayInSelection, False, None, True)
- self._timeStamp = CF.get_time_stamp()+nRounds
+ self._timeStamp = CF.get_time_stamp() + nRounds
self._nRounds = nRounds
def init(self):
- self._timeStamp = CF.get_time_stamp()+self._nRounds
+ self._timeStamp = CF.get_time_stamp() + self._nRounds
+
+ # keeping this local saves passing a reference to 'self' around
+ def make_sketchy(self, ve):
+ """
+ Creates the skeychy effect by causing the chain to run from
+ the start again. (loop over itself again)
+ """
+ if ve is None:
+ ve = self.current_edge
+ if ve.chaining_time_stamp == self._timeStamp:
+ return None
+ return ve
def traverse(self, iter):
- winner = None
it = AdjacencyIterator(iter)
- tvertex = self.next_vertex
- if type(tvertex) is TVertex:
- mateVE = tvertex.get_mate(self.current_edge)
- while not it.is_end:
- ve = it.object
- if ve.id == mateVE.id:
- winner = ve
- break
- it.increment()
- else:
- ## case of NonTVertex
- natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK,
- Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
- for i in range(len(natures)):
- currentNature = self.current_edge.nature
- if (natures[i] & currentNature) != 0:
- count=0
- while not it.is_end:
- visitNext = 0
- oNature = it.object.nature
- ve = it.object
- if ve.id == self.current_edge.id:
- it.increment()
- continue
- if (oNature & natures[i]) != 0:
- if (natures[i] != oNature) != 0:
- for j in range(i):
- if (natures[j] & oNature) != 0:
- visitNext = 1
- break
- if visitNext != 0:
- break
- count = count+1
- winner = ve
- it.increment()
- if count != 1:
- winner = None
- break
- if winner is None:
- winner = self.current_edge
- if winner.chaining_time_stamp == self._timeStamp:
- winner = None
- return winner
+ ## case of TVertex
+ vertex = self.next_vertex
+ if type(vertex) is TVertex:
+ mate = vertex.get_mate(self.current_edge)
+ return self.make_sketchy(find_matching_vertex(mate.id, it))
+ ## case of NonTVertex
+ winner = None
+ for i, nat in enumerate(NATURES):
+ if (nat & self.current_edge.nature):
+ for ve in it:
+ if ve.id == self.current_edge.id:
+ continue
+ ve_nat = ve.nature
+ if (ve_nat & nat):
+ if nat != ve_nat and nature_in_preceding(ve_nat, i):
+ break
+
+ if winner is not None:
+ return self.make_sketchy(None)
+
+ winner = ve
+ break
+ return self.make_sketchy(winner)
class pySketchyChainingIterator(ChainingIterator):
@@ -289,30 +274,30 @@ class pySketchyChainingIterator(ChainingIterator):
"""
def __init__(self, nRounds=3, stayInSelection=True):
ChainingIterator.__init__(self, stayInSelection, False, None, True)
- self._timeStamp = CF.get_time_stamp()+nRounds
+ self._timeStamp = CF.get_time_stamp() + nRounds
self._nRounds = nRounds
+ self.t = False
def init(self):
- self._timeStamp = CF.get_time_stamp()+self._nRounds
+ self._timeStamp = CF.get_time_stamp() + self._nRounds
def traverse(self, iter):
winner = None
found = False
- it = AdjacencyIterator(iter)
- while not it.is_end:
- ve = it.object
- if ve.id == self.current_edge.id:
+
+ for ve in AdjacencyIterator(iter):
+ if self.current_edge.id == ve.id:
found = True
- it.increment()
continue
winner = ve
- it.increment()
+
if not found:
# This is a fatal error condition: self.current_edge must be found
# among the edges seen by the AdjacencyIterator [bug #35695].
if bpy.app.debug_freestyle:
print('pySketchyChainingIterator: current edge not found')
return None
+
if winner is None:
winner = self.current_edge
if winner.chaining_time_stamp == self._timeStamp:
@@ -330,97 +315,61 @@ class pyFillOcclusionsRelativeChainingIterator(ChainingIterator):
def __init__(self, percent):
ChainingIterator.__init__(self, False, True, None, True)
- self._length = 0
+ self._length = 0.0
self._percent = float(percent)
+ self.timestamp = CF.get_time_stamp()
def init(self):
# A chain's length should preferably be evaluated only once.
# Therefore, the chain length is reset here.
- self._length = 0
+ self._length = 0.0
def traverse(self, iter):
winner = None
-
winnerOrientation = False
- winnerOrientation = 0
- #print(self.current_edge.id.first, self.current_edge.id.second)
it = AdjacencyIterator(iter)
- tvertex = self.next_vertex
- if type(tvertex) is TVertex:
- mateVE = tvertex.get_mate(self.current_edge)
- while not it.is_end:
- ve = it.object
- if ve.id == mateVE.id:
- winner = ve
- winnerOrientation = not it.is_incoming
- break
- it.increment()
+ ## case of TVertex
+ vertex = self.next_vertex
+ if type(vertex) is TVertex:
+ mate = vertex.get_mate(self.current_edge)
+ winner = find_matching_vertex(mate.id, it)
+ winnerOrientation = not it.is_incoming if not it.is_end else False
+ ## case of NonTVertex
else:
- ## case of NonTVertex
- natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK,
- Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
- for nat in natures:
- if (self.current_edge.nature & nat) != 0:
- count=0
- while not it.is_end:
- ve = it.object
- if (ve.nature & nat) != 0:
- count = count+1
+ for nat in NATURES:
+ if (self.current_edge.nature & nat):
+ for ve in it:
+ if (ve.nature & nat):
+ if winner is not None:
+ return None
winner = ve
winnerOrientation = not it.is_incoming
- it.increment()
- if count != 1:
- winner = None
break
- if winner is not None:
- # check whether this edge was part of the selection
- if winner.time_stamp != CF.get_time_stamp():
- #print("---", winner.id.first, winner.id.second)
- # if not, let's check whether it's short enough with
- # respect to the chain made without staying in the selection
- #------------------------------------------------------------
- # Did we compute the prospective chain length already ?
- if self._length == 0:
- #if not, let's do it
- _it = pyChainSilhouetteGenericIterator(False, False)
- _it.begin = winner
- _it.current_edge = winner
- _it.orientation = winnerOrientation
- _it.init()
- while not _it.is_end:
- ve = _it.object
- #print("--------", ve.id.first, ve.id.second)
- self._length = self._length + ve.length_2d
- _it.increment()
- if _it.is_begin:
- break;
- _it.begin = winner
- _it.current_edge = winner
- _it.orientation = winnerOrientation
- if not _it.is_begin:
- _it.decrement()
- while (not _it.is_end) and (not _it.is_begin):
- ve = _it.object
- #print("--------", ve.id.first, ve.id.second)
- self._length = self._length + ve.length_2d
- _it.decrement()
-
- # let's do the comparison:
- # nw let's compute the length of this connex non selected part:
- connexl = 0
- _cit = pyChainSilhouetteGenericIterator(False, False)
- _cit.begin = winner
- _cit.current_edge = winner
- _cit.orientation = winnerOrientation
- _cit.init()
- while _cit.is_end == 0 and _cit.object.time_stamp != CF.get_time_stamp():
- ve = _cit.object
- #print("-------- --------", ve.id.first, ve.id.second)
- connexl = connexl + ve.length_2d
- _cit.increment()
- if connexl > self._percent * self._length:
- winner = None
+ # check timestamp to see if this edge was part of the selection
+ if winner is not None and winner.time_stamp != self.timestamp:
+ # if the edge wasn't part of the selection, let's see
+ # whether it's short enough (with respect to self.percent)
+ # to be included.
+ if self._length == 0.0:
+ self._length = get_chain_length(winner, winnerOrientation)
+
+ # check if the gap can be bridged
+ connexl = 0.0
+ _cit = pyChainSilhouetteGenericIterator(False, False)
+ _cit.begin = winner
+ _cit.current_edge = winner
+ _cit.orientation = winnerOrientation
+ _cit.init()
+
+ while (not _cit.is_end) and _cit.object.time_stamp != self.timestamp:
+ connexl += _cit.object.length_2d
+ _cit.increment()
+ if _cit.is_begin: break
+
+ if connexl > self._percent * self._length:
+ return None
+
return winner
@@ -433,6 +382,7 @@ class pyFillOcclusionsAbsoluteChainingIterator(ChainingIterator):
def __init__(self, length):
ChainingIterator.__init__(self, False, True, None, True)
self._length = float(length)
+ self.timestamp = CF.get_time_stamp()
def init(self):
pass
@@ -440,53 +390,41 @@ class pyFillOcclusionsAbsoluteChainingIterator(ChainingIterator):
def traverse(self, iter):
winner = None
winnerOrientation = False
- #print(self.current_edge.id.first, self.current_edge.id.second)
it = AdjacencyIterator(iter)
- tvertex = self.next_vertex
- if type(tvertex) is TVertex:
- mateVE = tvertex.get_mate(self.current_edge)
- while not it.is_end:
- ve = it.object
- if ve.id == mateVE.id:
- winner = ve
- winnerOrientation = not it.is_incoming
- break
- it.increment()
+ ## case of TVertex
+ vertex = self.next_vertex
+ if type(vertex) is TVertex:
+ mate = vertex.get_mate(self.current_edge)
+ winner = find_matching_vertex(mate.id, it)
+ winnerOrientation = not it.is_incoming if not it.is_end else False
+ ## case of NonTVertex
else:
- ## case of NonTVertex
- natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK,
- Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
- for nat in natures:
- if (self.current_edge.nature & nat) != 0:
- count=0
- while not it.is_end:
- ve = it.object
- if (ve.nature & nat) != 0:
- count = count+1
+ for nat in NATURES:
+ if (self.current_edge.nature & nat):
+ for ve in it:
+ if (ve.nature & nat):
+ if winner is not None:
+ return None
winner = ve
winnerOrientation = not it.is_incoming
- it.increment()
- if count != 1:
- winner = None
break
- if winner is not None:
- # check whether this edge was part of the selection
- if winner.time_stamp != CF.get_time_stamp():
- #print("---", winner.id.first, winner.id.second)
- # nw let's compute the length of this connex non selected part:
- connexl = 0
- _cit = pyChainSilhouetteGenericIterator(False, False)
- _cit.begin = winner
- _cit.current_edge = winner
- _cit.orientation = winnerOrientation
- _cit.init()
- while _cit.is_end == 0 and _cit.object.time_stamp != CF.get_time_stamp():
- ve = _cit.object
- #print("-------- --------", ve.id.first, ve.id.second)
- connexl = connexl + ve.length_2d
- _cit.increment()
- if connexl > self._length:
- winner = None
+
+ if winner is not None and winner.time_stamp != self.timestamp:
+ connexl = 0.0
+ _cit = pyChainSilhouetteGenericIterator(False, False)
+ _cit.begin = winner
+ _cit.current_edge = winner
+ _cit.orientation = winnerOrientation
+ _cit.init()
+
+ while (not _cit.is_end) and _cit.object.time_stamp != self.timestamp:
+ connexl += _cit.object.length_2d
+ _cit.increment()
+ if _cit.is_begin: break
+
+ if connexl > self._length:
+ return None
+
return winner
@@ -500,7 +438,7 @@ class pyFillOcclusionsAbsoluteAndRelativeChainingIterator(ChainingIterator):
"""
def __init__(self, percent, l):
ChainingIterator.__init__(self, False, True, None, True)
- self._length = 0
+ self._length = 0.0
self._absLength = l
self._percent = float(percent)
@@ -508,88 +446,48 @@ class pyFillOcclusionsAbsoluteAndRelativeChainingIterator(ChainingIterator):
# each time we're evaluating a chain length
# we try to do it once. Thus we reinit
# the chain length here:
- self._length = 0
+ self._length = 0.0
def traverse(self, iter):
winner = None
winnerOrientation = False
- #print(self.current_edge.id.first, self.current_edge.id.second)
it = AdjacencyIterator(iter)
- tvertex = self.next_vertex
- if type(tvertex) is TVertex:
- mateVE = tvertex.get_mate(self.current_edge)
- while not it.is_end:
- ve = it.object
- if ve.id == mateVE.id:
- winner = ve
- winnerOrientation = not it.is_incoming
- break
- it.increment()
+ ## case of TVertex
+ vertex = self.next_vertex
+ if type(vertex) is TVertex:
+ mate = vertex.get_mate(self.current_edge)
+ winner = find_matching_vertex(mate.id, it)
+ winnerOrientation = not it.is_incoming if not it.is_end else False
+ ## case of NonTVertex
else:
- ## case of NonTVertex
- natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK,
- Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
- for nat in natures:
- if (self.current_edge.nature & nat) != 0:
- count=0
- while not it.is_end:
- ve = it.object
- if (ve.nature & nat) != 0:
- count = count+1
+ for nat in NATURES:
+ if (self.current_edge.nature & nat):
+ for ve in it:
+ if (ve.nature & nat):
+ if winner is not None:
+ return None
winner = ve
winnerOrientation = not it.is_incoming
- it.increment()
- if count != 1:
- winner = None
break
- if winner is not None:
- # check whether this edge was part of the selection
- if winner.time_stamp != CF.get_time_stamp():
- #print("---", winner.id.first, winner.id.second)
- # if not, let's check whether it's short enough with
- # respect to the chain made without staying in the selection
- #------------------------------------------------------------
- # Did we compute the prospective chain length already ?
- if self._length == 0:
- #if not, let's do it
- _it = pyChainSilhouetteGenericIterator(False, False)
- _it.begin = winner
- _it.current_edge = winner
- _it.orientation = winnerOrientation
- _it.init()
- while not _it.is_end:
- ve = _it.object
- #print("--------", ve.id.first, ve.id.second)
- self._length = self._length + ve.length_2d
- _it.increment()
- if _it.is_begin:
- break;
- _it.begin = winner
- _it.current_edge = winner
- _it.orientation = winnerOrientation
- if not _it.is_begin:
- _it.decrement()
- while (not _it.is_end) and (not _it.is_begin):
- ve = _it.object
- #print("--------", ve.id.first, ve.id.second)
- self._length = self._length + ve.length_2d
- _it.decrement()
-
- # let's do the comparison:
- # nw let's compute the length of this connex non selected part:
- connexl = 0
+
+ if winner is not None and winner.time_stamp != CF.get_time_stamp():
+
+ if self._length == 0.0:
+ self._length = get_chain_length(winner, winnerOrientation)
+
+ connexl = 0.0
_cit = pyChainSilhouetteGenericIterator(False, False)
_cit.begin = winner
_cit.current_edge = winner
_cit.orientation = winnerOrientation
_cit.init()
- while _cit.is_end == 0 and _cit.object.time_stamp != CF.get_time_stamp():
- ve = _cit.object
- #print("-------- --------", ve.id.first, ve.id.second)
- connexl = connexl + ve.length_2d
+ while (not _cit.is_end) and _cit.object.time_stamp != CF.get_time_stamp():
+ connexl += _cit.object.length_2d
_cit.increment()
+ if _cit.is_begin: break
+
if (connexl > self._percent * self._length) or (connexl > self._absLength):
- winner = None
+ return None
return winner
@@ -603,96 +501,55 @@ class pyFillQi0AbsoluteAndRelativeChainingIterator(ChainingIterator):
"""
def __init__(self, percent, l):
ChainingIterator.__init__(self, False, True, None, True)
- self._length = 0
+ self._length = 0.0
self._absLength = l
- self._percent = float(percent)
+ self._percent = percent
def init(self):
# A chain's length should preverably be evaluated only once.
# Therefore, the chain length is reset here.
- self._length = 0
+ self._length = 0.0
def traverse(self, iter):
winner = None
winnerOrientation = False
-
- #print(self.current_edge.id.first, self.current_edge.id.second)
it = AdjacencyIterator(iter)
- tvertex = self.next_vertex
- if type(tvertex) is TVertex:
- mateVE = tvertex.get_mate(self.current_edge)
- while not it.is_end:
- ve = it.object
- if ve.id == mateVE.id:
- winner = ve
- winnerOrientation = not it.is_incoming
- break
- it.increment()
+ ## case of TVertex
+ vertex = self.next_vertex
+ if type(vertex) is TVertex:
+ mate = vertex.get_mate(self.current_edge)
+ winner = find_matching_vertex(mate.id, it)
+ winnerOrientation = not it.is_incoming if not it.is_end else False
+ ## case of NonTVertex
else:
- ## case of NonTVertex
- natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK,
- Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
- for nat in natures:
- if (self.current_edge.nature & nat) != 0:
- count=0
- while not it.is_end:
- ve = it.object
- if (ve.nature & nat) != 0:
- count = count+1
+ for nat in NATURES:
+ if (self.current_edge.nature & nat):
+ for ve in it:
+ if (ve.nature & nat):
+ if winner is not None:
+ return None
winner = ve
winnerOrientation = not it.is_incoming
- it.increment()
- if count != 1:
- winner = None
break
- if winner is not None:
- # check whether this edge was part of the selection
- if winner.qi != 0:
- #print("---", winner.id.first, winner.id.second)
- # if not, let's check whether it's short enough with
- # respect to the chain made without staying in the selection
- #------------------------------------------------------------
- # Did we compute the prospective chain length already ?
- if self._length == 0:
- #if not, let's do it
- _it = pyChainSilhouetteGenericIterator(False, False)
- _it.begin = winner
- _it.current_edge = winner
- _it.orientation = winnerOrientation
- _it.init()
- while not _it.is_end:
- ve = _it.object
- #print("--------", ve.id.first, ve.id.second)
- self._length = self._length + ve.length_2d
- _it.increment()
- if _it.is_begin:
- break;
- _it.begin = winner
- _it.current_edge = winner
- _it.orientation = winnerOrientation
- if not _it.is_begin:
- _it.decrement()
- while (not _it.is_end) and (not _it.is_begin):
- ve = _it.object
- #print("--------", ve.id.first, ve.id.second)
- self._length = self._length + ve.length_2d
- _it.decrement()
-
- # let's do the comparison:
- # nw let's compute the length of this connex non selected part:
+
+ if winner is not None and winner.qi:
+
+
+ if self._length == 0.0:
+ self._length = get_chain_length(winner, winnerOrientation)
+
connexl = 0
_cit = pyChainSilhouetteGenericIterator(False, False)
_cit.begin = winner
_cit.current_edge = winner
_cit.orientation = winnerOrientation
_cit.init()
- while not _cit.is_end and _cit.object.qi != 0:
- ve = _cit.object
- #print("-------- --------", ve.id.first, ve.id.second)
- connexl = connexl + ve.length_2d
+ while (not _cit.is_end) and _cit.object.qi != 0:
+ connexl += _cit.object.length_2d
_cit.increment()
+ if _cit.is_begin: break
if (connexl > self._percent * self._length) or (connexl > self._absLength):
- winner = None
+ return None
return winner
@@ -717,63 +574,44 @@ class pyNoIdChainSilhouetteIterator(ChainingIterator):
def traverse(self, iter):
winner = None
it = AdjacencyIterator(iter)
- tvertex = self.next_vertex
- if type(tvertex) is TVertex:
- mateVE = tvertex.get_mate(self.current_edge)
- while not it.is_end:
- ve = it.object
- feB = self.current_edge.last_fedge
- feA = ve.first_fedge
- vB = feB.second_svertex
- vA = feA.first_svertex
+ # case of TVertex
+ vertex = self.next_vertex
+ if type(vertex) is TVertex:
+ for ve in it:
+ # case one
+ vA = self.current_edge.last_fedge.second_svertex
+ vB = ve.first_fedge.first_svertex
if vA.id.first == vB.id.first:
- winner = ve
- break
- feA = self.current_edge.first_fedge
- feB = ve.last_fedge
- vB = feB.second_svertex
- vA = feA.first_svertex
+ return ve
+ # case two
+ vA = self.current_edge.first_fedge.first_svertex
+ vB = ve.last_fedge.second_svertex
if vA.id.first == vB.id.first:
- winner = ve
- break
- feA = self.current_edge.last_fedge
- feB = ve.last_fedge
- vB = feB.second_svertex
- vA = feA.second_svertex
+ return ve
+ # case three
+ vA = self.current_edge.last_fedge.second_svertex
+ vB = ve.last_fedge.second_svertex
if vA.id.first == vB.id.first:
- winner = ve
- break
- feA = self.current_edge.first_fedge
- feB = ve.first_fedge
- vB = feB.first_svertex
- vA = feA.first_svertex
+ return ve
+ # case four
+ vA = self.current_edge.first_fedge.first_svertex
+ vB = ve.first_fedge.first_svertex
if vA.id.first == vB.id.first:
- winner = ve
- break
- it.increment()
+ return ve
+ return None
+ ## case of NonTVertex
else:
- ## case of NonTVertex
- natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK,
- Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
- for i in range(len(natures)):
- currentNature = self.current_edge.nature
- if (natures[i] & currentNature) != 0:
- count=0
- while not it.is_end:
- visitNext = 0
- oNature = it.object.nature
- if (oNature & natures[i]) != 0:
- if natures[i] != oNature:
- for j in range(i):
- if (natures[j] & oNature) != 0:
- visitNext = 1
- break
- if visitNext != 0:
- break
- count = count+1
- winner = it.object
- it.increment()
- if count != 1:
- winner = None
- break
- return winner
+ for i, nat in enumerate(NATURES):
+ if (nat & self.current_edge.nature):
+ for ve in it:
+ ve_nat = ve.nature
+ if (ve_nat & nat):
+ if (nat != ve_nat) and any(n & ve_nat for n in NATURES[:i]):
+ break
+
+ if winner is not None:
+ return
+
+ winner = ve
+ return winner
+ return None
diff --git a/release/scripts/freestyle/modules/freestyle/functions.py b/release/scripts/freestyle/modules/freestyle/functions.py
index 379e933862c..773d04ddeab 100644
--- a/release/scripts/freestyle/modules/freestyle/functions.py
+++ b/release/scripts/freestyle/modules/freestyle/functions.py
@@ -91,8 +91,8 @@ from freestyle.utils import integrate
from mathutils import Vector
-## Functions for 0D elements (vertices)
-#######################################
+
+# -- Functions for 0D elements (vertices) -- #
class CurveMaterialF0D(UnaryFunction0DMaterial):
@@ -104,7 +104,7 @@ class CurveMaterialF0D(UnaryFunction0DMaterial):
cp = inter.object
assert(isinstance(cp, CurvePoint))
fe = cp.first_svertex.get_fedge(cp.second_svertex)
- assert(fe is not None)
+ assert(fe is not None), "CurveMaterialF0D: fe is None"
return fe.material if fe.is_smooth else fe.material_left
@@ -140,11 +140,7 @@ class pyDensityAnisotropyF0D(UnaryFunction0DDouble):
c_3 = self.d3Density(inter)
cMax = max(max(c_0,c_1), max(c_2,c_3))
cMin = min(min(c_0,c_1), min(c_2,c_3))
- if c_iso == 0:
- v = 0
- else:
- v = (cMax-cMin)/c_iso
- return v
+ return 0 if (c_iso == 0) else (cMax-cMin) / c_iso
class pyViewMapGradientVectorF0D(UnaryFunction0DVec2f):
@@ -161,9 +157,9 @@ class pyViewMapGradientVectorF0D(UnaryFunction0DVec2f):
def __call__(self, iter):
p = iter.object.point_2d
gx = CF.read_complete_view_map_pixel(self._l, int(p.x+self._step), int(p.y)) - \
- CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
+ CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
gy = CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y+self._step)) - \
- CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
+ CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
return Vector((gx, gy))
@@ -171,19 +167,18 @@ class pyViewMapGradientNormF0D(UnaryFunction0DDouble):
def __init__(self, l):
UnaryFunction0DDouble.__init__(self)
self._l = l
- self._step = pow(2,self._l)
+ self._step = pow(2, self._l)
def __call__(self, iter):
p = iter.object.point_2d
- gx = CF.read_complete_view_map_pixel(self._l, int(p.x+self._step), int(p.y)) - \
- CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
- gy = CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y+self._step)) - \
- CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
- grad = Vector((gx, gy))
- return grad.length
+ gx = CF.read_complete_view_map_pixel(self._l, int(p.x + self._step), int(p.y)) - \
+ CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
+ gy = CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y + self._step)) - \
+ CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
+ return Vector((gx, gy)).length
+
-## Functions for 1D elements (curves)
-#####################################
+# -- Functions for 1D elements (curves) -- #
class pyGetInverseProjectedZF1D(UnaryFunction1DDouble):
diff --git a/release/scripts/freestyle/modules/freestyle/predicates.py b/release/scripts/freestyle/modules/freestyle/predicates.py
index ce6f0a35ffe..fede3e3e2da 100644
--- a/release/scripts/freestyle/modules/freestyle/predicates.py
+++ b/release/scripts/freestyle/modules/freestyle/predicates.py
@@ -51,6 +51,7 @@ from freestyle.types import (
TVertex,
UnaryPredicate0D,
UnaryPredicate1D,
+ Id,
)
from freestyle.functions import (
Curvature2DAngleF0D,
@@ -70,14 +71,15 @@ from freestyle.functions import (
pyDensityAnisotropyF1D,
pyViewMapGradientNormF1D,
)
+
import random
-## Unary predicates for 0D elements (vertices)
-##############################################
+# -- Unary predicates for 0D elements (vertices) -- #
+
class pyHigherCurvature2DAngleUP0D(UnaryPredicate0D):
- def __init__(self,a):
+ def __init__(self, a):
UnaryPredicate0D.__init__(self)
self._a = a
@@ -88,15 +90,15 @@ class pyHigherCurvature2DAngleUP0D(UnaryPredicate0D):
class pyUEqualsUP0D(UnaryPredicate0D):
- def __init__(self,u, w):
+ def __init__(self, u, w):
UnaryPredicate0D.__init__(self)
self._u = u
self._w = w
+ self._func = pyCurvilinearLengthF0D()
def __call__(self, inter):
- func = pyCurvilinearLengthF0D()
- u = func(inter)
- return (u > (self._u-self._w)) and (u < (self._u+self._w))
+ u = self._func(inter)
+ return (u > (self._u - self._w)) and (u < (self._u + self._w))
class pyVertexNatureUP0D(UnaryPredicate0D):
@@ -105,26 +107,23 @@ class pyVertexNatureUP0D(UnaryPredicate0D):
self._nature = nature
def __call__(self, inter):
- v = inter.object
- return (v.nature & self._nature) != 0
+ return bool(inter.object.nature & self._nature)
-## check whether an Interface0DIterator
-## is a TVertex and is the one that is
-## hidden (inferred from the context)
class pyBackTVertexUP0D(UnaryPredicate0D):
+ """
+ Check whether an Interface0DIterator
+ references a TVertex and is the one that is
+ hidden (inferred from the context)
+ """
def __init__(self):
UnaryPredicate0D.__init__(self)
self._getQI = QuantitativeInvisibilityF0D()
def __call__(self, iter):
- if (iter.object.nature & Nature.T_VERTEX) == 0:
+ if not (iter.object.nature & Nature.T_VERTEX) or iter.is_end:
return False
- if iter.is_end:
- return False
- if self._getQI(iter) != 0:
- return True
- return False
+ return self._getQI(iter) != 0
class pyParameterUP0DGoodOne(UnaryPredicate0D):
@@ -135,7 +134,7 @@ class pyParameterUP0DGoodOne(UnaryPredicate0D):
def __call__(self, inter):
u = inter.u
- return ((u>=self._m) and (u<=self._M))
+ return ((u >= self._m) and (u <= self._M))
class pyParameterUP0D(UnaryPredicate0D):
@@ -143,36 +142,39 @@ class pyParameterUP0D(UnaryPredicate0D):
UnaryPredicate0D.__init__(self)
self._m = pmin
self._M = pmax
+ self._func = Curvature2DAngleF0D()
def __call__(self, inter):
- func = Curvature2DAngleF0D()
- c = func(inter)
- b1 = (c>0.1)
+ c = self._func(inter)
+ b1 = (c > 0.1)
u = inter.u
- b = ((u>=self._m) and (u<=self._M))
- return b and b1
+ b = ((u >= self._m) and (u <= self._M))
+ return (b and b1)
+
+
+# -- Unary predicates for 1D elements (curves) -- #
-## Unary predicates for 1D elements (curves)
-############################################
class AndUP1D(UnaryPredicate1D):
- def __init__(self, pred1, pred2):
+ def __init__(self, *predicates):
UnaryPredicate1D.__init__(self)
- self.__pred1 = pred1
- self.__pred2 = pred2
+ self.predicates = predicates
+ if len(self.predicates) < 2:
+ raise ValueError("Expected two or more UnaryPredicate1D")
def __call__(self, inter):
- return self.__pred1(inter) and self.__pred2(inter)
+ return all(pred(inter) for pred in self.predicates)
class OrUP1D(UnaryPredicate1D):
- def __init__(self, pred1, pred2):
+ def __init__(self, *predicates):
UnaryPredicate1D.__init__(self)
- self.__pred1 = pred1
- self.__pred2 = pred2
+ self.predicates = predicates
+ if len(self.predicates) < 2:
+ raise ValueError("Expected two or more UnaryPredicate1D")
def __call__(self, inter):
- return self.__pred1(inter) or self.__pred2(inter)
+ return any(pred(inter) for pred in self.predicates)
class NotUP1D(UnaryPredicate1D):
@@ -184,6 +186,29 @@ class NotUP1D(UnaryPredicate1D):
return not self.__pred(inter)
+class ObjectNamesUP1D(UnaryPredicate1D):
+ def __init__(self, names, negative=False):
+ UnaryPredicate1D.__init__(self)
+ self._names = names
+ self._negative = negative
+
+ def __call__(self, viewEdge):
+ found = viewEdge.viewshape.name in self._names
+ return found if not self._negative else not found
+
+
+class QuantitativeInvisibilityRangeUP1D(UnaryPredicate1D):
+ def __init__(self, qi_start, qi_end):
+ UnaryPredicate1D.__init__(self)
+ self.__getQI = QuantitativeInvisibilityF1D()
+ self.__qi_start = qi_start
+ self.__qi_end = qi_end
+
+ def __call__(self, inter):
+ qi = self.__getQI(inter)
+ return (self.__qi_start <= qi <= self.__qi_end)
+
+
class pyNFirstUP1D(UnaryPredicate1D):
def __init__(self, n):
UnaryPredicate1D.__init__(self)
@@ -191,14 +216,12 @@ class pyNFirstUP1D(UnaryPredicate1D):
self.__count = 0
def __call__(self, inter):
- self.__count = self.__count + 1
- if self.__count <= self.__n:
- return True
- return False
+ self.__count += 1
+ return (self.__count <= self.__n)
class pyHigherLengthUP1D(UnaryPredicate1D):
- def __init__(self,l):
+ def __init__(self, l):
UnaryPredicate1D.__init__(self)
self._l = l
@@ -213,28 +236,20 @@ class pyNatureUP1D(UnaryPredicate1D):
self._getNature = CurveNatureF1D()
def __call__(self, inter):
- if(self._getNature(inter) & self._nature):
- return True
- return False
+ return bool(self._getNature(inter) & self._nature)
class pyHigherNumberOfTurnsUP1D(UnaryPredicate1D):
- def __init__(self,n,a):
+ def __init__(self, n, a):
UnaryPredicate1D.__init__(self)
self._n = n
self._a = a
def __call__(self, inter):
- count = 0
func = Curvature2DAngleF0D()
it = inter.vertices_begin()
- while not it.is_end:
- if func(it) > self._a:
- count = count+1
- if count > self._n:
- return True
- it.increment()
- return False
+ # sum the turns, check against n
+ return sum(1 for ve in it if func(it) > self._a) > self._n
class pyDensityUP1D(UnaryPredicate1D):
@@ -278,9 +293,7 @@ class pyHighSteerableViewMapDensityUP1D(UnaryPredicate1D):
def __init__(self, threshold, level, integration=IntegrationType.MEAN):
UnaryPredicate1D.__init__(self)
self._threshold = threshold
- self._level = level
- self._integration = integration
- self._func = GetSteerableViewMapDensityF1D(self._level, self._integration)
+ self._func = GetSteerableViewMapDensityF1D(level, integration)
def __call__(self, inter):
return (self._func(inter) > self._threshold)
@@ -290,24 +303,17 @@ class pyHighDirectionalViewMapDensityUP1D(UnaryPredicate1D):
def __init__(self, threshold, orientation, level, integration=IntegrationType.MEAN, sampling=2.0):
UnaryPredicate1D.__init__(self)
self._threshold = threshold
- self._orientation = orientation
- self._level = level
- self._integration = integration
- self._sampling = sampling
+ self._func = GetDirectionalViewMapDensityF1D(orientation, level, integration, sampling)
def __call__(self, inter):
- func = GetDirectionalViewMapDensityF1D(self._orientation, self._level, self._integration, self._sampling)
- return (func(inter) > self._threshold)
+ return (self.func(inter) > self._threshold)
class pyHighViewMapDensityUP1D(UnaryPredicate1D):
def __init__(self, threshold, level, integration=IntegrationType.MEAN, sampling=2.0):
UnaryPredicate1D.__init__(self)
self._threshold = threshold
- self._level = level
- self._integration = integration
- self._sampling = sampling
- self._func = GetCompleteViewMapDensityF1D(self._level, self._integration, self._sampling) # 2.0 is the smpling
+ self._func = GetCompleteViewMapDensityF1D(level, integration, sampling)
def __call__(self, inter):
return (self._func(inter) > self._threshold)
@@ -316,67 +322,56 @@ class pyHighViewMapDensityUP1D(UnaryPredicate1D):
class pyDensityFunctorUP1D(UnaryPredicate1D):
def __init__(self, wsize, threshold, functor, funcmin=0.0, funcmax=1.0, integration=IntegrationType.MEAN):
UnaryPredicate1D.__init__(self)
- self._wsize = wsize
self._threshold = float(threshold)
self._functor = functor
self._funcmin = float(funcmin)
self._funcmax = float(funcmax)
- self._integration = integration
+ self._func = DensityF1D(wsize, integration)
def __call__(self, inter):
- func = DensityF1D(self._wsize, self._integration)
res = self._functor(inter)
- k = (res-self._funcmin)/(self._funcmax-self._funcmin)
+ k = (res - self._funcmin) / (self._funcmax - self._funcmin)
return (func(inter) < (self._threshold * k))
class pyZSmallerUP1D(UnaryPredicate1D):
- def __init__(self,z, integration=IntegrationType.MEAN):
+ def __init__(self, z, integration=IntegrationType.MEAN):
UnaryPredicate1D.__init__(self)
self._z = z
- self._integration = integration
+ self.func = GetProjectedZF1D(integration)
def __call__(self, inter):
- func = GetProjectedZF1D(self._integration)
- return (func(inter) < self._z)
+ return (self.func(inter) < self._z)
class pyIsOccludedByUP1D(UnaryPredicate1D):
def __init__(self,id):
UnaryPredicate1D.__init__(self)
+ if not isinstance(id, Id):
+ raise TypeError("pyIsOccludedByUP1D expected freestyle.types.Id, not " + type(id).__name__)
self._id = id
def __call__(self, inter):
- func = GetShapeF1D()
- shapes = func(inter)
- for s in shapes:
- if(s.id == self._id):
- return False
+ shapes = GetShapeF1D()(inter)
+ if any(s.id == self._id for s in shapes):
+ return False
+
+ # construct iterators
it = inter.vertices_begin()
itlast = inter.vertices_end()
itlast.decrement()
- v = it.object
- vlast = itlast.object
- tvertex = v.viewvertex
- if type(tvertex) is TVertex:
- #print("TVertex: [ ", tvertex.id.first, ",", tvertex.id.second," ]")
- eit = tvertex.edges_begin()
- while not eit.is_end:
- ve, incoming = eit.object
- if ve.id == self._id:
- return True
- #print("-------", ve.id.first, "-", ve.id.second)
- eit.increment()
- tvertex = vlast.viewvertex
- if type(tvertex) is TVertex:
- #print("TVertex: [ ", tvertex.id.first, ",", tvertex.id.second," ]")
+
+ vertex = next(it)
+ if type(vertex) is TVertex:
+ eit = vertex.edges_begin()
+ if any(ve.id == self._id for (ve, incoming) in eit):
+ return True
+
+ vertex = next(itlast)
+ if type(vertex) is TVertex:
eit = tvertex.edges_begin()
- while not eit.is_end:
- ve, incoming = eit.object
- if ve.id == self._id:
- return True
- #print("-------", ve.id.first, "-", ve.id.second)
- eit.increment()
+ if any(ve.id == self._id for (ve, incoming) in eit):
+ return True
return False
@@ -386,12 +381,8 @@ class pyIsInOccludersListUP1D(UnaryPredicate1D):
self._id = id
def __call__(self, inter):
- func = GetOccludersF1D()
- occluders = func(inter)
- for a in occluders:
- if a.id == self._id:
- return True
- return False
+ occluders = GetOccludersF1D()(inter)
+ return any(a.id == self._id for a in occluders)
class pyIsOccludedByItselfUP1D(UnaryPredicate1D):
@@ -403,11 +394,7 @@ class pyIsOccludedByItselfUP1D(UnaryPredicate1D):
def __call__(self, inter):
lst1 = self.__func1(inter)
lst2 = self.__func2(inter)
- for vs1 in lst1:
- for vs2 in lst2:
- if vs1.id == vs2.id:
- return True
- return False
+ return any(vs1.id == vs2.id for vs1 in lst1 for vs2 in lst2)
class pyIsOccludedByIdListUP1D(UnaryPredicate1D):
@@ -417,27 +404,17 @@ class pyIsOccludedByIdListUP1D(UnaryPredicate1D):
self.__func1 = GetOccludersF1D()
def __call__(self, inter):
- lst1 = self.__func1(inter)
- for vs1 in lst1:
- for _id in self._idlist:
- if vs1.id == _id:
- return True
- return False
+ lst1 = self.__func1(inter.object)
+ return any(vs1.id == _id for vs1 in lst1 for _id in self._idlist)
class pyShapeIdListUP1D(UnaryPredicate1D):
def __init__(self,idlist):
UnaryPredicate1D.__init__(self)
- self._idlist = idlist
- self._funcs = []
- for _id in idlist:
- self._funcs.append(ShapeUP1D(_id.first, _id.second))
+ self._funcs = tuple(ShapeUP1D(_id, 0) for _id in idlist)
def __call__(self, inter):
- for func in self._funcs:
- if func(inter) == 1:
- return True
- return False
+ return any(func(inter) for func in self._funcs)
## deprecated
@@ -447,12 +424,8 @@ class pyShapeIdUP1D(UnaryPredicate1D):
self._id = _id
def __call__(self, inter):
- func = GetShapeF1D()
- shapes = func(inter)
- for a in shapes:
- if a.id == self._id:
- return True
- return False
+ shapes = GetShapeF1D()(inter)
+ return any(a.id == self._id for a in shapes)
class pyHighDensityAnisotropyUP1D(UnaryPredicate1D):
@@ -473,7 +446,6 @@ class pyHighViewMapGradientNormUP1D(UnaryPredicate1D):
def __call__(self, inter):
gn = self._GetGradient(inter)
- #print(gn)
return (gn > self._threshold)
@@ -503,53 +475,50 @@ class pyClosedCurveUP1D(UnaryPredicate1D):
it = inter.vertices_begin()
itlast = inter.vertices_end()
itlast.decrement()
- vlast = itlast.object
- v = it.object
- #print(v.id.first, v.id.second)
- #print(vlast.id.first, vlast.id.second)
- if v.id == vlast.id:
- return True
- return False
+ return (next(it).id == next(itlast).id)
+
+
+# -- Binary predicates for 1D elements (curves) -- #
-## Binary predicates for 1D elements (curves)
-#############################################
class AndBP1D(BinaryPredicate1D):
- def __init__(self, pred1, pred2):
+ def __init__(self, *predicates):
BinaryPredicate1D.__init__(self)
- self.__pred1 = pred1
- self.__pred2 = pred2
+ self._predicates = predicates
+ if len(self.predicates) < 2:
+ raise ValueError("Expected two or more BinaryPredicate1D")
- def __call__(self, inter1, inter2):
- return self.__pred1(inter1, inter2) and self.__pred2(inter1, inter2)
+ def __call__(self, i1, i2):
+ return all(pred(i1, i2) for pred in self._predicates)
class OrBP1D(BinaryPredicate1D):
- def __init__(self, pred1, pred2):
+ def __init__(self, *predicates):
BinaryPredicate1D.__init__(self)
- self.__pred1 = pred1
- self.__pred2 = pred2
+ self._predicates = predicates
+ if len(self.predicates) < 2:
+ raise ValueError("Expected two or more BinaryPredicate1D")
- def __call__(self, inter1, inter2):
- return self.__pred1(inter1, inter2) or self.__pred2(inter1, inter2)
+ def __call__(self, i1, i2):
+ return any(pred(i1, i2) for pred in self._predicates)
class NotBP1D(BinaryPredicate1D):
- def __init__(self, pred):
+ def __init__(self, predicate):
BinaryPredicate1D.__init__(self)
- self.__pred = pred
+ self._predicate = predicate
- def __call__(self, inter1, inter2):
- return not self.__pred(inter1, inter2)
+ def __call__(self, i1, i2):
+ return (not self._precicate(i1, i2))
class pyZBP1D(BinaryPredicate1D):
def __init__(self, iType=IntegrationType.MEAN):
BinaryPredicate1D.__init__(self)
- self._GetZ = GetZF1D(iType)
+ self.func = GetZF1D(iType)
def __call__(self, i1, i2):
- return (self._GetZ(i1) > self._GetZ(i2))
+ return (self.func(i1) > self.func(i2))
class pyZDiscontinuityBP1D(BinaryPredicate1D):
@@ -569,10 +538,10 @@ class pyLengthBP1D(BinaryPredicate1D):
class pySilhouetteFirstBP1D(BinaryPredicate1D):
def __call__(self, inter1, inter2):
bpred = SameShapeIdBP1D()
- if (bpred(inter1, inter2) != 1):
+ if (not bpred(inter1, inter2)):
return False
if (inter1.nature & Nature.SILHOUETTE):
- return (inter2.nature & Nature.SILHOUETTE) != 0
+ return bool(inter2.nature & Nature.SILHOUETTE)
return (inter1.nature == inter2.nature)
@@ -587,16 +556,13 @@ class pyViewMapGradientNormBP1D(BinaryPredicate1D):
self._GetGradient = pyViewMapGradientNormF1D(l, IntegrationType.MEAN)
def __call__(self, i1,i2):
- #print("compare gradient")
return (self._GetGradient(i1) > self._GetGradient(i2))
class pyShuffleBP1D(BinaryPredicate1D):
def __init__(self):
BinaryPredicate1D.__init__(self)
- random.seed(1)
+ random.seed = 1
def __call__(self, inter1, inter2):
- r1 = random.uniform(0,1)
- r2 = random.uniform(0,1)
- return (r1<r2)
+ return (random.uniform(0,1) < random.uniform(0,1))
diff --git a/release/scripts/freestyle/modules/freestyle/shaders.py b/release/scripts/freestyle/modules/freestyle/shaders.py
index 8e9d77915ef..8a9faf26017 100644
--- a/release/scripts/freestyle/modules/freestyle/shaders.py
+++ b/release/scripts/freestyle/modules/freestyle/shaders.py
@@ -62,6 +62,7 @@ from freestyle.types import (
StrokeAttribute,
StrokeShader,
StrokeVertexIterator,
+ StrokeVertex,
)
from freestyle.functions import (
Curvature2DAngleF0D,
@@ -74,16 +75,27 @@ from freestyle.functions import (
)
from freestyle.predicates import (
pyVertexNatureUP0D,
+ pyUEqualsUP0D,
)
+
+from freestyle.utils import (
+ bound,
+ bounding_box,
+ phase_to_direction,
+ )
+
from freestyle.utils import ContextFunctions as CF
-from math import atan, cos, pi, pow, sin, sinh, sqrt
+import bpy
+import random
+
+from math import atan, cos, pi, sin, sinh, sqrt
from mathutils import Vector
from random import randint
-## thickness modifiers
-######################
+# -- Thickness Stroke Shaders -- #
+
class pyDepthDiscontinuityThicknessShader(StrokeShader):
"""
@@ -92,21 +104,16 @@ class pyDepthDiscontinuityThicknessShader(StrokeShader):
"""
def __init__(self, min, max):
StrokeShader.__init__(self)
- self.__min = float(min)
- self.__max = float(max)
- self.__func = ZDiscontinuityF0D()
+ self.a = max - min
+ self.b = min
+ self.func = ZDiscontinuityF0D()
def shade(self, stroke):
- z_min=0.0
- z_max=1.0
- a = (self.__max - self.__min)/(z_max-z_min)
- b = (self.__min*z_max-self.__max*z_min)/(z_max-z_min)
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- z = self.__func(Interface0DIterator(it))
- thickness = a*z+b
- it.object.attribute.thickness = (thickness, thickness)
- it.increment()
+ it = Interface0DIterator(stroke)
+ for svert in it:
+ z = self.func(it)
+ thickness = self.a * z + self.b
+ svert.attribute.thickness = (thickness, thickness)
class pyConstantThicknessShader(StrokeShader):
@@ -115,14 +122,11 @@ class pyConstantThicknessShader(StrokeShader):
"""
def __init__(self, thickness):
StrokeShader.__init__(self)
- self._thickness = thickness
+ self._thickness = thickness / 2.0
def shade(self, stroke):
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- t = self._thickness/2.0
- it.object.attribute.thickness = (t, t)
- it.increment()
+ for svert in stroke:
+ svert.attribute.thickness = (self._thickness, self._thickness)
class pyFXSVaryingThicknessWithDensityShader(StrokeShader):
@@ -131,28 +135,22 @@ class pyFXSVaryingThicknessWithDensityShader(StrokeShader):
"""
def __init__(self, wsize, threshold_min, threshold_max, thicknessMin, thicknessMax):
StrokeShader.__init__(self)
- self.wsize= wsize
- self.threshold_min= threshold_min
- self.threshold_max= threshold_max
+ self._func = DensityF0D(wsize)
+ self.threshold_min = threshold_min
+ self.threshold_max = threshold_max
self._thicknessMin = thicknessMin
self._thicknessMax = thicknessMax
def shade(self, stroke):
- n = stroke.stroke_vertices_size()
- i = 0
- it = stroke.stroke_vertices_begin()
- func = DensityF0D(self.wsize)
- while not it.is_end:
- c = func(Interface0DIterator(it))
- if c < self.threshold_min:
- c = self.threshold_min
- if c > self.threshold_max:
- c = self.threshold_max
-## t = (c - self.threshold_min)/(self.threshold_max - self.threshold_min)*(self._thicknessMax-self._thicknessMin) + self._thicknessMin
- t = (self.threshold_max - c )/(self.threshold_max - self.threshold_min)*(self._thicknessMax-self._thicknessMin) + self._thicknessMin
- it.object.attribute.thickness = (t/2.0, t/2.0)
- i = i+1
- it.increment()
+ it = Interface0DIterator(stroke)
+ delta_threshold = self.threshold_max - self.threshold_min
+ delta_thickness = self._thicknessMax - self._thicknessMin
+
+ for svert in it:
+ c = self._func(it)
+ c = bound(self.threshold_min, c, self.threshold_max)
+ t = (self.threshold_max - c) / delta_threshold * delta_thickness + self._thicknessMin
+ svert.attribute.thickness = (t / 2.0, t / 2.0)
class pyIncreasingThicknessShader(StrokeShader):
@@ -165,18 +163,14 @@ class pyIncreasingThicknessShader(StrokeShader):
self._thicknessMax = thicknessMax
def shade(self, stroke):
- n = stroke.stroke_vertices_size()
- i = 0
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- c = float(i)/float(n)
- if i < float(n)/2.0:
- t = (1.0 - c)*self._thicknessMin + c * self._thicknessMax
+ n = len(stroke)
+ for i, svert in enumerate(stroke):
+ c = i / n
+ if i < (n * 0.5):
+ t = (1.0 - c) * self._thicknessMin + c * self._thicknessMax
else:
- t = (1.0 - c)*self._thicknessMax + c * self._thicknessMin
- it.object.attribute.thickness = (t/2.0, t/2.0)
- i = i+1
- it.increment()
+ t = (1.0 - c) * self._thicknessMax + c * self._thicknessMin
+ svert.attribute.thickness = (t / 2.0, t / 2.0)
class pyConstrainedIncreasingThicknessShader(StrokeShader):
@@ -191,28 +185,20 @@ class pyConstrainedIncreasingThicknessShader(StrokeShader):
self._ratio = ratio
def shade(self, stroke):
- slength = stroke.length_2d
- tmp = self._ratio*slength
- maxT = 0.0
- if tmp < self._thicknessMax:
- maxT = tmp
- else:
- maxT = self._thicknessMax
- n = stroke.stroke_vertices_size()
- i = 0
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- att = it.object.attribute
- c = float(i)/float(n)
- if i < float(n)/2.0:
- t = (1.0 - c)*self._thicknessMin + c * maxT
+ n = len(stroke)
+ maxT = min(self._ratio * stroke.length_2d, self._thicknessMax)
+
+ for i, svert in enumerate(stroke):
+ c = i / n
+ if i < (n * 0.5):
+ t = (1.0 - c) * self._thicknessMin + c * maxT
else:
- t = (1.0 - c)*maxT + c * self._thicknessMin
- att.thickness = (t/2.0, t/2.0)
- if i == n-1:
- att.thickness = (self._thicknessMin/2.0, self._thicknessMin/2.0)
- i = i+1
- it.increment()
+ t = (1.0 - c) * maxT + c * self._thicknessMin
+
+ if i == (n - 1):
+ svert.attribute.thickness = (self._thicknessMin / 2.0, self._thicknessMin / 2.0)
+ else:
+ svert.attribute.thickness = (t / 2.0, t / 2.0)
class pyDecreasingThicknessShader(StrokeShader):
@@ -226,21 +212,14 @@ class pyDecreasingThicknessShader(StrokeShader):
def shade(self, stroke):
l = stroke.length_2d
- tMax = self._thicknessMax
- if self._thicknessMax > 0.33*l:
- tMax = 0.33*l
- tMin = self._thicknessMin
- if self._thicknessMin > 0.1*l:
- tMin = 0.1*l
- n = stroke.stroke_vertices_size()
- i = 0
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- c = float(i)/float(n)
- t = (1.0 - c)*tMax +c*tMin
- it.object.attribute.thickness = (t/2.0, t/2.0)
- i = i+1
- it.increment()
+ n = len(stroke)
+ tMax = min(self._thicknessMax, 0.33 * l)
+ tMin = min(self._thicknessMin, 0.10 * l)
+
+ for i, svert in enumerate(stroke):
+ c = i / n
+ t = (1.0 - c) * tMax + c * tMin
+ svert.attribute.thickness = (t / 2.0, t / 2.0)
class pyNonLinearVaryingThicknessShader(StrokeShader):
@@ -248,28 +227,18 @@ class pyNonLinearVaryingThicknessShader(StrokeShader):
Assigns thickness to a stroke based on an exponential function
"""
def __init__(self, thicknessExtremity, thicknessMiddle, exponent):
- StrokeShader.__init__(self)
self._thicknessMin = thicknessMiddle
self._thicknessMax = thicknessExtremity
- self._exponent = exponent
+ self._exp = exponent
+ StrokeShader.__init__(self)
def shade(self, stroke):
- n = stroke.stroke_vertices_size()
- i = 0
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- if i < float(n)/2.0:
- c = float(i)/float(n)
- else:
- c = float(n-i)/float(n)
- c = self.smoothC(c, self._exponent)
- t = (1.0 - c)*self._thicknessMax + c * self._thicknessMin
- it.object.attribute.thickness = (t/2.0, t/2.0)
- i = i+1
- it.increment()
-
- def smoothC(self, a, exp):
- return pow(float(a), exp) * pow(2.0, exp)
+ n = len(stroke)
+ for i, svert in enumerate(stroke):
+ c = (i / n) if (i < n / 2.0) else ((n - i) / n)
+ c = pow(c, self._exp) * pow(2.0, self._exp)
+ t = (1.0 - c) * self._thicknessMax + c * self._thicknessMin
+ svert.attribute.thickness = (t / 2.0, t / 2.0)
class pySLERPThicknessShader(StrokeShader):
@@ -280,29 +249,23 @@ class pySLERPThicknessShader(StrokeShader):
StrokeShader.__init__(self)
self._thicknessMin = thicknessMin
self._thicknessMax = thicknessMax
- self._omega = omega
+ self.omega = omega
def shade(self, stroke):
- slength = stroke.length_2d
- tmp = 0.33*slength
- maxT = self._thicknessMax
- if tmp < self._thicknessMax:
- maxT = tmp
- n = stroke.stroke_vertices_size()
- i = 0
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- c = float(i)/float(n)
- if i < float(n)/2.0:
- t = sin((1-c)*self._omega)/sinh(self._omega)*self._thicknessMin + sin(c*self._omega)/sinh(self._omega) * maxT
+ n = len(stroke)
+ maxT = min(self._thicknessMax, 0.33 * stroke.length_2d)
+ omega = self.omega
+ sinhyp = sinh(omega)
+ for i, svert in enumerate(stroke):
+ c = i / n
+ if i < (n * 0.5):
+ t = sin((1-c) * omega) / sinhyp * self._thicknessMin + sin(c * omega) / sinhyp * maxT
else:
- t = sin((1-c)*self._omega)/sinh(self._omega)*maxT + sin(c*self._omega)/sinh(self._omega) * self._thicknessMin
- it.object.attribute.thickness = (t/2.0, t/2.0)
- i = i+1
- it.increment()
+ t = sin((1-c) * omega) / sinhyp * maxT + sin(c * omega) / sinhyp * self._thicknessMin
+ svert.attribute.thickness = (t / 2.0, t / 2.0)
-class pyTVertexThickenerShader(StrokeShader): ## FIXME
+class pyTVertexThickenerShader(StrokeShader):
"""
Thickens TVertices (visual intersections between two edges)
"""
@@ -312,46 +275,22 @@ class pyTVertexThickenerShader(StrokeShader): ## FIXME
self._n = n
def shade(self, stroke):
- it = stroke.stroke_vertices_begin()
- predTVertex = pyVertexNatureUP0D(Nature.T_VERTEX)
- while not it.is_end:
- if predTVertex(it) == 1:
- it2 = StrokeVertexIterator(it)
- it2.increment()
- if not (it.is_begin or it2.is_end):
- it.increment()
- continue
- n = self._n
- a = self._a
- if it.is_begin:
- it3 = StrokeVertexIterator(it)
- count = 0
- while (not it3.is_end) and count < n:
- att = it3.object.attribute
- (tr, tl) = att.thickness
- r = (a-1.0)/float(n-1)*(float(n)/float(count+1) - 1) + 1
- #r = (1.0-a)/float(n-1)*count + a
- att.thickness = (r*tr, r*tl)
- it3.increment()
- count = count + 1
- if it2.is_end:
- it4 = StrokeVertexIterator(it)
- count = 0
- while (not it4.is_begin) and count < n:
- att = it4.object.attribute
- (tr, tl) = att.thickness
- r = (a-1.0)/float(n-1)*(float(n)/float(count+1) - 1) + 1
- #r = (1.0-a)/float(n-1)*count + a
- att.thickness = (r*tr, r*tl)
- it4.decrement()
- count = count + 1
- if it4.is_begin:
- att = it4.object.attribute
- (tr, tl) = att.thickness
- r = (a-1.0)/float(n-1)*(float(n)/float(count+1) - 1) + 1
- #r = (1.0-a)/float(n-1)*count + a
- att.thickness = (r*tr, r*tl)
- it.increment()
+ n = self._n
+ a = self._a
+
+ term = (a - 1.0) / (n - 1.0)
+
+ if (stroke[0].nature & Nature.T_VERTEX):
+ for count, svert in zip(range(n), stroke):
+ r = term * (n / (count + 1.0) - 1.0) + 1.0
+ (tr, tl) = svert.attribute.thickness
+ svert.attribute.thickness = (r * tr, r * tl)
+
+ if (stroke[-1].nature & Nature.T_VERTEX):
+ for count, svert in zip(range(n), reversed(stroke)):
+ r = term * (n / (count + 1.0) - 1.0) + 1.0
+ (tr, tl) = svert.attribute.thickness
+ svert.attribute.thickness = (r * tr, r * tl)
class pyImportance2DThicknessShader(StrokeShader):
@@ -362,26 +301,18 @@ class pyImportance2DThicknessShader(StrokeShader):
"""
def __init__(self, x, y, w, kmin, kmax):
StrokeShader.__init__(self)
- self._x = x
- self._y = y
- self._w = float(w)
- self._kmin = float(kmin)
- self._kmax = float(kmax)
+ self._origin = Vector((x, y))
+ self._w = w
+ self._kmin, self._kmax = kmin, kmax
def shade(self, stroke):
- origin = Vector((self._x, self._y))
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- v = it.object
- d = (v.point_2d - self._origin).length
- if d > self._w:
- k = self._kmin
- else:
- k = (self._kmax*(self._w-d) + self._kmin*d)/self._w
- att = v.attribute
- (tr, tl) = att.thickness
- att.thickness = (k*tr/2.0, k*tl/2.0)
- it.increment()
+ for svert in stroke:
+ d = (svert.point_2d - self._origin).length
+ k = (self._kmin if (d > self._w) else
+ (self._kmax * (self._w-d) + self._kmin * d) / self._w)
+
+ (tr, tl) = svert.attribute.thickness
+ svert.attribute.thickness = (k*tr/2.0, k*tl/2.0)
class pyImportance3DThicknessShader(StrokeShader):
@@ -390,28 +321,18 @@ class pyImportance3DThicknessShader(StrokeShader):
"""
def __init__(self, x, y, z, w, kmin, kmax):
StrokeShader.__init__(self)
- self._x = x
- self._y = y
- self._z = z
- self._w = float(w)
- self._kmin = float(kmin)
- self._kmax = float(kmax)
+ self._origin = Vector((x, y, z))
+ self._w = w
+ self._kmin, self._kmax = kmin, kmax
def shade(self, stroke):
- origin = Vector((self._x, self._y, self._z))
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- v = it.object
- p = v.point_3d
- d = (p-origin).length
- if d > self._w:
- k = self._kmin
- else:
- k = (self._kmax*(self._w-d) + self._kmin*d)/self._w
- att = v.attribute
- (tr, tl) = att.thickness
- att.thickness = (k*tr/2.0, k*tl/2.0)
- it.increment()
+ for svert in stroke:
+ d = (svert.point_3d - self._origin).length
+ k = (self._kmin if (d > self._w) else
+ (self._kmax * (self._w-d) + self._kmin * d) / self._w)
+
+ (tr, tl) = svert.attribute.thickness
+ svert.attribute.thickness = (k*tr/2.0, k*tl/2.0)
class pyZDependingThicknessShader(StrokeShader):
@@ -423,49 +344,35 @@ class pyZDependingThicknessShader(StrokeShader):
StrokeShader.__init__(self)
self.__min = min
self.__max = max
- self.__func = GetProjectedZF0D()
+ self.func = GetProjectedZF0D()
def shade(self, stroke):
- it = stroke.stroke_vertices_begin()
- z_min = 1
- z_max = 0
- while not it.is_end:
- z = self.__func(Interface0DIterator(it))
- if z < z_min:
- z_min = z
- if z > z_max:
- z_max = z
- it.increment()
+ it = Interface0DIterator(stroke)
+ z_indices = tuple(self.func(it) for _ in it)
+ z_min, z_max = min(1, *z_indices), max(0, *z_indices)
z_diff = 1 / (z_max - z_min)
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- z = (self.__func(Interface0DIterator(it)) - z_min) * z_diff
+
+ for svert, z_index in zip(stroke, z_indices):
+ z = (z_index - z_min) * z_diff
thickness = (1 - z) * self.__max + z * self.__min
- it.object.attribute.thickness = (thickness, thickness)
- it.increment()
+ svert.attribute.thickness = (thickness, thickness)
+
+# -- Color & Alpha Stroke Shaders -- #
-## color modifiers
-##################
class pyConstantColorShader(StrokeShader):
"""
Assigns a constant color to the stroke
"""
- def __init__(self, r, g, b, a=1):
+ def __init__(self,r,g,b, a = 1):
StrokeShader.__init__(self)
- self._r = r
- self._g = g
- self._b = b
+ self._color = (r, g, b)
self._a = a
-
def shade(self, stroke):
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- att = it.object.attribute
- att.color = (self._r, self._g, self._b)
- att.alpha = self._a
- it.increment()
+ for svert in stroke:
+ svert.attribute.color = self._color
+ svert.attribute.alpha = self._a
class pyIncreasingColorShader(StrokeShader):
@@ -474,23 +381,18 @@ class pyIncreasingColorShader(StrokeShader):
"""
def __init__(self,r1,g1,b1,a1, r2,g2,b2,a2):
StrokeShader.__init__(self)
- self._c1 = [r1,g1,b1,a1]
- self._c2 = [r2,g2,b2,a2]
+ # use 4d vector to simplify math
+ self._c1 = Vector((r1, g1 ,b1, a1))
+ self._c2 = Vector((r2, g2, b2, a2))
def shade(self, stroke):
- n = stroke.stroke_vertices_size() - 1
- inc = 0
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- att = it.object.attribute
- c = float(inc) / float(n)
-
- att.color = ((1.0 - c) * self._c1[0] + c * self._c2[0],
- (1.0 - c) * self._c1[1] + c * self._c2[1],
- (1.0 - c) * self._c1[2] + c * self._c2[2])
- att.alpha = (1.0 - c) * self._c1[3] + c * self._c2[3]
- inc = inc + 1
- it.increment()
+ n = len(stroke) - 1
+
+ for i, svert in enumerate(stroke):
+ c = i / n
+ color = (1 - c) * self._c1 + c * self._c2
+ svert.attribute.color = color[:3]
+ svert.attribute.alpha = color[3]
class pyInterpolateColorShader(StrokeShader):
@@ -499,23 +401,32 @@ class pyInterpolateColorShader(StrokeShader):
"""
def __init__(self,r1,g1,b1,a1, r2,g2,b2,a2):
StrokeShader.__init__(self)
- self._c1 = [r1,g1,b1,a1]
- self._c2 = [r2,g2,b2,a2]
+ # use 4d vector to simplify math
+ self._c1 = Vector((r1, g1 ,b1, a1))
+ self._c2 = Vector((r2, g2, b2, a2))
def shade(self, stroke):
- n = stroke.stroke_vertices_size() - 1
- inc = 0
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- att = it.object.attribute
- u = float(inc) / float(n)
- c = 1.0 - 2.0 * abs(u - 0.5)
- att.color = ((1.0 - c) * self._c1[0] + c * self._c2[0],
- (1.0 - c) * self._c1[1] + c * self._c2[1],
- (1.0 - c) * self._c1[2] + c * self._c2[2])
- att.alpha = (1.0-c) * self._c1[3] + c * self._c2[3]
- inc = inc+1
- it.increment()
+ n = len(stroke) - 1
+ for i, svert in enumerate(stroke):
+ c = 1.0 - 2.0 * abs((i / n) - 0.5)
+ color = (1.0 - c) * self._c1 + c * self._c2
+ svert.attribute.color = color[:3]
+ svert.attribute.alpha = color[3]
+
+
+class pyModulateAlphaShader(StrokeShader):
+ """
+ Limits the stroke's alpha between a min and max value.
+ """
+ def __init__(self, min=0, max=1):
+ StrokeShader.__init__(self)
+ self.__min = min
+ self.__max = max
+ def shade(self, stroke):
+ for svert in stroke:
+ alpha = svert.attribute.alpha
+ alpha = bound(self.__min, alpha * svert.point.y * 0.0025, self.__max)
+ svert.attribute.alpha = alpha
class pyMaterialColorShader(StrokeShader):
@@ -525,61 +436,59 @@ class pyMaterialColorShader(StrokeShader):
def __init__(self, threshold=50):
StrokeShader.__init__(self)
self._threshold = threshold
+ self._func = MaterialF0D()
def shade(self, stroke):
- it = stroke.stroke_vertices_begin()
- func = MaterialF0D()
xn = 0.312713
yn = 0.329016
Yn = 1.0
- un = 4.* xn / (-2.*xn + 12.*yn + 3.)
- vn= 9.* yn / (-2.*xn + 12.*yn +3.)
- while not it.is_end:
- mat = func(Interface0DIterator(it))
-
- r = mat.diffuse[0]
- g = mat.diffuse[1]
- b = mat.diffuse[2]
-
- X = 0.412453*r + 0.35758 *g + 0.180423*b
- Y = 0.212671*r + 0.71516 *g + 0.072169*b
- Z = 0.019334*r + 0.119193*g + 0.950227*b
-
- if (X, Y, Z) == (0, 0, 0):
- X = 0.01
- Y = 0.01
- Z = 0.01
- u = 4.*X / (X + 15.*Y + 3.*Z)
- v = 9.*Y / (X + 15.*Y + 3.*Z)
-
- L= 116. * pow((Y/Yn),(1./3.)) -16
+ un = 4.0 * xn / (-2.0 * xn + 12.0 * yn + 3.0)
+ vn = 9.0 * yn / (-2.0 * xn + 12.0 * yn + 3.0)
+
+ it = Interface0DIterator(stroke)
+ for svert in it:
+ mat = self._func(it)
+
+ r, g, b, *_ = mat.diffuse
+
+ X = 0.412453 * r + 0.35758 * g + 0.180423 * b
+ Y = 0.212671 * r + 0.71516 * g + 0.072169 * b
+ Z = 0.019334 * r + 0.11919 * g + 0.950227 * b
+
+ if not any((X, Y, Z)):
+ X = Y = Z = 0.01
+
+ u = 4.0 * X / (X + 15.0 * Y + 3.0 * Z)
+ v = 9.0 * Y / (X + 15.0 * Y + 3.0 * Z)
+
+ L= 116. * pow((Y/Yn),(1./3.)) - 16
U = 13. * L * (u - un)
V = 13. * L * (v - vn)
if L > self._threshold:
- L = L/1.3
- U = U+10
+ L /= 1.3
+ U += 10.
else:
- L = L +2.5*(100-L)/5.
- U = U/3.0
- V = V/3.0
- u = U / (13. * L) + un
- v = V / (13. * L) + vn
+ L = L + 2.5 * (100-L) * 0.2
+ U /= 3.0
+ V /= 3.0
+
+ u = U / (13.0 * L) + un
+ v = V / (13.0 * L) + vn
Y = Yn * pow(((L+16.)/116.), 3.)
- X = -9.0 * Y * u / ((u - 4.0) * v - u * v)
- Z = (9.0 * Y - 15.0 * v * Y - v * X) / (3.0 * v)
+ X = -9. * Y * u / ((u - 4.)* v - u * v)
+ Z = (9. * Y - 15*v*Y - v*X) /( 3. * v)
r = 3.240479 * X - 1.53715 * Y - 0.498535 * Z
g = -0.969256 * X + 1.875991 * Y + 0.041556 * Z
b = 0.055648 * X - 0.204043 * Y + 1.057311 * Z
- r = max(0,r)
- g = max(0,g)
- b = max(0,b)
+ r = max(0, r)
+ g = max(0, g)
+ b = max(0, b)
- it.object.attribute.color = (r, g, b)
- it.increment()
+ svert.attribute.color = (r, g, b)
class pyRandomColorShader(StrokeShader):
@@ -588,18 +497,14 @@ class pyRandomColorShader(StrokeShader):
"""
def __init__(self, s=1):
StrokeShader.__init__(self)
- random.seed(s)
+ random.seed = s
def shade(self, stroke):
- ## pick a random color
- c0 = float(random.uniform(15,75))/100.0
- c1 = float(random.uniform(15,75))/100.0
- c2 = float(random.uniform(15,75))/100.0
- #print(c0, c1, c2)
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- it.object.attribute.color = (c0,c1,c2)
- it.increment()
+ c = (random.uniform(15, 75) * 0.01,
+ random.uniform(15, 75) * 0.01,
+ random.uniform(15, 75) * 0.01)
+ for svert in stroke:
+ svert.attribute.color = c
class py2DCurvatureColorShader(StrokeShader):
@@ -608,15 +513,14 @@ class py2DCurvatureColorShader(StrokeShader):
A higher curvature will yield a brighter color
"""
def shade(self, stroke):
- it = stroke.stroke_vertices_begin()
func = Curvature2DAngleF0D()
- while not it.is_end:
- c = func(Interface0DIterator(it))
- if c < 0:
- print("negative 2D curvature")
- color = 10.0 * c/3.1415
- it.object.attribute.color = (color, color, color)
- it.increment()
+ it = Interface0DIterator(stroke)
+ for svert in it:
+ c = func(it)
+ if c < 0 and bpy.app.debug_freestyle:
+ print("py2DCurvatureColorShader: negative 2D curvature")
+ color = 10.0 * c / pi
+ svert.attribute.color = (color, color, color)
class pyTimeColorShader(StrokeShader):
@@ -627,13 +531,13 @@ class pyTimeColorShader(StrokeShader):
def __init__(self, step=0.01):
StrokeShader.__init__(self)
self._step = step
-
def shade(self, stroke):
- for i, svert in enumerate(iter(stroke)):
+ for i, svert in enumerate(stroke):
c = i * self._step
- svert.attribute.color = (c,c,c)
+ svert.attribute.color = (c, c, c)
-## geometry modifiers
+
+# -- Geometry Stroke Shaders -- #
class pySamplingShader(StrokeShader):
@@ -659,89 +563,55 @@ class pyBackboneStretcherShader(StrokeShader):
self._l = l
def shade(self, stroke):
- it0 = stroke.stroke_vertices_begin()
- it1 = StrokeVertexIterator(it0)
- it1.increment()
- itn = stroke.stroke_vertices_end()
- itn.decrement()
- itn_1 = StrokeVertexIterator(itn)
- itn_1.decrement()
- v0 = it0.object
- v1 = it1.object
- vn_1 = itn_1.object
- vn = itn.object
- p0 = v0.point_2d
- pn = vn.point_2d
- p1 = v1.point_2d
- pn_1 = vn_1.point_2d
- d1 = (p0 - p1).normalized()
- dn = (pn - pn_1).normalized()
- newFirst = p0+d1*float(self._l)
- newLast = pn+dn*float(self._l)
- v0.point = newFirst
- vn.point = newLast
+ # get start and end points
+ v0, vn = stroke[0], stroke[-1]
+ p0, pn = v0.point, vn.point
+ # get the direction
+ d1 = (p0 - stroke[ 1].point).normalized()
+ dn = (pn - stroke[-2].point).normalized()
+ v0.point += d1 * self._l
+ vn.point += dn * self._l
stroke.update_length()
class pyLengthDependingBackboneStretcherShader(StrokeShader):
"""
Stretches the stroke's backbone proportional to the stroke's length
+ NOTE: you'll probably want an l somewhere between (0.5 - 0). A value that
+ is too high may yield unexpected results.
"""
def __init__(self, l):
StrokeShader.__init__(self)
self._l = l
-
def shade(self, stroke):
- l = stroke.length_2d
- stretch = self._l*l
- it0 = stroke.stroke_vertices_begin()
- it1 = StrokeVertexIterator(it0)
- it1.increment()
- itn = stroke.stroke_vertices_end()
- itn.decrement()
- itn_1 = StrokeVertexIterator(itn)
- itn_1.decrement()
- v0 = it0.object
- v1 = it1.object
- vn_1 = itn_1.object
- vn = itn.object
- p0 = v0.point_2d
- pn = vn.point_2d
- p1 = v1.point_2d
- pn_1 = vn_1.point_2d
- d1 = (p0 - p1).normalized()
- dn = (pn - pn_1).normalized()
- newFirst = p0+d1*float(stretch)
- newLast = pn+dn*float(stretch)
- v0.point = newFirst
- vn.point = newLast
+ # get start and end points
+ v0, vn = stroke[0], stroke[-1]
+ p0, pn = v0.point, vn.point
+ # get the direction
+ d1 = (p0 - stroke[ 1].point).normalized()
+ dn = (pn - stroke[-2].point).normalized()
+ v0.point += d1 * self._l * stroke.length_2d
+ vn.point += dn * self._l * stroke.length_2d
stroke.update_length()
-
class pyGuidingLineShader(StrokeShader):
- """
- Replaces the stroke by its corresponding tangent
- """
def shade(self, stroke):
- it = stroke.stroke_vertices_begin() ## get the first vertex
- itlast = stroke.stroke_vertices_end() ##
- itlast.decrement() ## get the last one
- t = itlast.object.point - it.object.point ## tangent direction
- itmiddle = StrokeVertexIterator(it) ##
- while itmiddle.object.u < 0.5: ## look for the stroke middle vertex
- itmiddle.increment() ##
- it = StrokeVertexIterator(itmiddle)
- it.increment()
- while not it.is_end: ## position all the vertices along the tangent for the right part
- it.object.point = itmiddle.object.point+t*(it.object.u-itmiddle.object.u)
- it.increment()
+ # get the tangent direction
+ t = stroke[-1].point - stroke[0].point
+ # look for the stroke middle vertex
+ itmiddle = iter(stroke)
+ while itmiddle.object.u < 0.5:
+ itmiddle.increment()
+ center_vertex = itmiddle.object
+ # position all the vertices along the tangent for the right part
it = StrokeVertexIterator(itmiddle)
- it.decrement()
- while not it.is_begin: ## position all the vertices along the tangent for the left part
- it.object.point = itmiddle.object.point-t*(itmiddle.object.u-it.object.u)
- it.decrement()
- it.object.point = itmiddle.object.point-t*itmiddle.object.u ## first vertex
+ for svert in it:
+ svert.point = center_vertex.point + t * (svert.u - center_vertex.u)
+ # position all the vertices along the tangent for the left part
+ it = StrokeVertexIterator(itmiddle).reversed()
+ for svert in it:
+ svert.point = center_vertex.point - t * (center_vertex.u - svert.u)
stroke.update_length()
@@ -754,25 +624,18 @@ class pyBackboneStretcherNoCuspShader(StrokeShader):
self._l = l
def shade(self, stroke):
- it0 = stroke.stroke_vertices_begin()
- it1 = StrokeVertexIterator(it0)
- it1.increment()
- itn = stroke.stroke_vertices_end()
- itn.decrement()
- itn_1 = StrokeVertexIterator(itn)
- itn_1.decrement()
- v0 = it0.object
- v1 = it1.object
- if (v0.nature & Nature.CUSP) == 0 and (v1.nature & Nature.CUSP) == 0:
+
+ v0, v1 = stroke[0], stroke[1]
+ vn, vn_1 = stroke[-1], stroke[-2]
+
+ if not (v0.nature & v1.nature & Nature.CUSP):
d1 = (v0.point - v1.point).normalized()
- newFirst = v0.point+d1*float(self._l)
- v0.point = newFirst
- vn_1 = itn_1.object
- vn = itn.object
- if (vn.nature & Nature.CUSP) == 0 and (vn_1.nature & Nature.CUSP) == 0:
+ v0.point += d1 * self._l
+
+ if not (vn.nature & vn_1.nature & Nature.CUSP):
dn = (vn.point - vn_1.point).normalized()
- newLast = vn.point + dn * float(self._l)
- vn.point = newLast
+ vn.point += dn * self._l
+
stroke.update_length()
@@ -792,13 +655,9 @@ class pyDiffusion2Shader(StrokeShader):
def shade(self, stroke):
for i in range (1, self._nbIter):
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- v = it.object
- p1 = v.point
- p2 = self._normalInfo(Interface0DIterator(it))*self._lambda*self._curvatureInfo(Interface0DIterator(it))
- v.point = p1+p2
- it.increment()
+ it = Interface0DIterator(stroke)
+ for svert in it:
+ svert.point += self._normalInfo(it) * self._lambda * self._curvatureInfo(it)
stroke.update_length()
@@ -810,33 +669,36 @@ class pyTipRemoverShader(StrokeShader):
StrokeShader.__init__(self)
self._l = l
+ @staticmethod
+ def check_vertex(v, length):
+ """
+ Returns True if the given strokevertex is less than self._l away
+ from the stroke's tip and therefore should be removed.
+ """
+ return (v.curvilinear_abscissa < length or v.stroke_length-v.curvilinear_abscissa < length)
+
def shade(self, stroke):
- originalSize = stroke.stroke_vertices_size()
- if originalSize < 4:
+ n = len(stroke)
+ if n < 4:
return
- verticesToRemove = []
- oldAttributes = []
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- v = it.object
- if v.curvilinear_abscissa < self._l or v.stroke_length-v.curvilinear_abscissa < self._l:
- verticesToRemove.append(v)
- oldAttributes.append(StrokeAttribute(v.attribute))
- it.increment()
- if originalSize-len(verticesToRemove) < 2:
+
+ verticesToRemove = tuple(svert for svert in stroke if self.check_vertex(svert, self._l))
+ # explicit conversion to StrokeAttribute is needed
+ oldAttributes = (StrokeAttribute(svert.attribute) for svert in stroke)
+
+ if n - len(verticesToRemove) < 2:
return
+
for sv in verticesToRemove:
stroke.remove_vertex(sv)
+
stroke.update_length()
- stroke.resample(originalSize)
- if stroke.stroke_vertices_size() != originalSize:
+ stroke.resample(n)
+ if len(stroke) != n and bpy.app.debug_freestyle:
print("pyTipRemover: Warning: resampling problem")
- it = stroke.stroke_vertices_begin()
- for a in oldAttributes:
- if it.is_end:
- break
- it.object.attribute = a
- it.increment()
+
+ for svert, a in zip(stroke, oldAttributes):
+ svert.attribute = a
stroke.update_length()
@@ -845,28 +707,15 @@ class pyTVertexRemoverShader(StrokeShader):
Removes t-vertices from the stroke
"""
def shade(self, stroke):
- if stroke.stroke_vertices_size() <= 3:
+ if len(stroke) < 4:
return
- predTVertex = pyVertexNatureUP0D(Nature.T_VERTEX)
- it = stroke.stroke_vertices_begin()
- itlast = stroke.stroke_vertices_end()
- itlast.decrement()
- if predTVertex(it):
- stroke.remove_vertex(it.object)
- if predTVertex(itlast):
- stroke.remove_vertex(itlast.object)
- stroke.update_length()
-
-#class pyExtremitiesOrientationShader(StrokeShader):
-# def __init__(self, x1,y1,x2=0,y2=0):
-# StrokeShader.__init__(self)
-# self._v1 = Vector((x1,y1))
-# self._v2 = Vector((x2,y2))
-# def shade(self, stroke):
-# #print(self._v1.x,self._v1.y)
-# stroke.setBeginningOrientation(self._v1.x,self._v1.y)
-# stroke.setEndingOrientation(self._v2.x,self._v2.y)
+ v0, vn = stroke[0], stroke[-1]
+ if (v0.nature & Nature.T_VERTEX):
+ stroke.remove_vertex(v0)
+ if (vn.nature & Nature.T_VERTEX):
+ stroke.remove_vertex(vn)
+ stroke.update_length()
class pyHLRShader(StrokeShader):
@@ -875,115 +724,14 @@ class pyHLRShader(StrokeShader):
based on hidden line removal (HLR)
"""
def shade(self, stroke):
- originalSize = stroke.stroke_vertices_size()
- if originalSize < 4:
+ if len(stroke) < 4:
return
- it = stroke.stroke_vertices_begin()
- invisible = 0
- it2 = StrokeVertexIterator(it)
- it2.increment()
- fe = self.get_fedge(it.object, it2.object)
- if fe.viewedge.qi != 0:
- invisible = 1
- while not it2.is_end:
- v = it.object
- vnext = it2.object
- if (v.nature & Nature.VIEW_VERTEX) != 0:
- #if (v.nature & Nature.T_VERTEX) != 0:
- fe = self.get_fedge(v, vnext)
- qi = fe.viewedge.qi
- if qi != 0:
- invisible = 1
- else:
- invisible = 0
- if invisible:
- v.attribute.visible = False
- it.increment()
- it2.increment()
-
- def get_fedge(self, it1, it2):
- return it1.get_fedge(it2)
-
-# broken and a mess
-class pyTVertexOrientationShader(StrokeShader):
- def __init__(self):
- StrokeShader.__init__(self)
- self._Get2dDirection = Orientation2DF1D()
- ## finds the TVertex orientation from the TVertex and
- ## the previous or next edge
-
- def findOrientation(self, tv, ve):
- mateVE = tv.get_mate(ve)
- if ve.qi != 0 or mateVE.qi != 0:
- ait = AdjacencyIterator(tv,1,0)
- winner = None
- incoming = True
- while not ait.is_end:
- ave = ait.object
- if ave.id != ve.id and ave.id != mateVE.id:
- winner = ait.object
- if not ait.isIncoming(): # FIXME
- incoming = False
- break
- ait.increment()
- if winner is not None:
- if not incoming:
- direction = self._Get2dDirection(winner.last_fedge)
- else:
- direction = self._Get2dDirection(winner.first_fedge)
- return direction
- return None
-
- def castToTVertex(self, cp):
- if cp.t2d() == 0.0:
- return cp.first_svertex.viewvertex
- elif cp.t2d() == 1.0:
- return cp.second_svertex.viewvertex
- return None
-
- def shade(self, stroke):
- it = stroke.stroke_vertices_begin()
- it2 = StrokeVertexIterator(it)
- it2.increment()
- ## case where the first vertex is a TVertex
- v = it.object
- if (v.nature & Nature.T_VERTEX) != 0:
- tv = self.castToTVertex(v)
- if tv is not None:
- ve = self.get_fedge(v, it2.object).viewedge
- dir = self.findOrientation(tv, ve)
- if dir is not None:
- #print(dir.x, dir.y)
- v.attribute.set_attribute_vec2("orientation", dir)
- while not it2.is_end:
- vprevious = it.object
- v = it2.object
- if (v.nature & Nature.T_VERTEX) != 0:
- tv = self.castToTVertex(v)
- if tv is not None:
- ve = self.get_fedge(vprevious, v).viewedge
- dir = self.findOrientation(tv, ve)
- if dir is not None:
- #print(dir.x, dir.y)
- v.attribute.set_attribute_vec2("orientation", dir)
- it.increment()
- it2.increment()
- ## case where the last vertex is a TVertex
- v = it.object
- if (v.nature & Nature.T_VERTEX) != 0:
- itPrevious = StrokeVertexIterator(it)
- itPrevious.decrement()
- tv = self.castToTVertex(v)
- if tv is not None:
- ve = self.get_fedge(itPrevious.object, v).viewedge
- dir = self.findOrientation(tv, ve)
- if dir is not None:
- #print(dir.x, dir.y)
- v.attribute.set_attribute_vec2("orientation", dir)
-
- def get_fedge(self, it1, it2):
- return it1.get_fedge(it2)
+ it = iter(stroke)
+ for v1, v2 in zip(it, it.incremented()):
+ if (v1.nature & Nature.VIEW_VERTEX):
+ visible = (v1.get_fedge(v2).viewedge.qi != 0)
+ v1.attribute.visible = not visible
class pySinusDisplacementShader(StrokeShader):
@@ -997,19 +745,12 @@ class pySinusDisplacementShader(StrokeShader):
self._getNormal = Normal2DF0D()
def shade(self, stroke):
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- v = it.object
- #print(self._getNormal.name)
- n = self._getNormal(Interface0DIterator(it))
- p = v.point
- u = v.u
- a = self._a*(1-2*(abs(u-0.5)))
- n = n*a*cos(self._f*u*6.28)
- #print(n.x, n.y)
- v.point = p+n
- #v.point = v.point+n*a*cos(f*v.u)
- it.increment()
+ it = Interface0DIterator(stroke)
+ for svert in it:
+ normal = self._getNormal(it)
+ a = self._a * (1 - 2 * (abs(svert.u - 0.5)))
+ n = normal * a * cos(self._f * svert.u * 6.28)
+ svert.point += n
stroke.update_length()
@@ -1027,13 +768,10 @@ class pyPerlinNoise1DShader(StrokeShader):
self.__oct = oct
def shade(self, stroke):
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- v = it.object
- i = v.projected_x + v.projected_y
- nres = self.__noise.turbulence1(i, self.__freq, self.__amp, self.__oct)
- v.point = (v.projected_x + nres, v.projected_y + nres)
- it.increment()
+ for svert in stroke:
+ s = svert.projected_x + svert.projected_y
+ nres = self.__noise.turbulence1(s, self.__freq, self.__amp, self.__oct)
+ svert.point = (svert.projected_x + nres, svert.projected_y + nres)
stroke.update_length()
@@ -1053,12 +791,9 @@ class pyPerlinNoise2DShader(StrokeShader):
self.__oct = oct
def shade(self, stroke):
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- v = it.object
- nres = self.__noise.turbulence2(v.point_2d, self.__freq, self.__amp, self.__oct)
- v.point = (v.projected_x + nres, v.projected_y + nres)
- it.increment()
+ for svert in stroke:
+ nres = self.__noise.turbulence2(svert.point_2d, self.__freq, self.__amp, self.__oct)
+ svert.point = (svert.projected_x + nres, svert.projected_y + nres)
stroke.update_length()
@@ -1073,66 +808,52 @@ class pyBluePrintCirclesShader(StrokeShader):
self.__random_radius = random_radius
def shade(self, stroke):
- it = stroke.stroke_vertices_begin()
- if it.is_end:
- return
- p_min = it.object.point.copy()
- p_max = it.object.point.copy()
- while not it.is_end:
- p = it.object.point
- if p.x < p_min.x:
- p_min.x = p.x
- if p.x > p_max.x:
- p_max.x = p.x
- if p.y < p_min.y:
- p_min.y = p.y
- if p.y > p_max.y:
- p_max.y = p.y
- it.increment()
+ # get minimum and maximum coordinates
+ p_min, p_max = bounding_box(stroke)
+
stroke.resample(32 * self.__turns)
- sv_nb = stroke.stroke_vertices_size()
-# print("min :", p_min.x, p_min.y) # DEBUG
-# print("mean :", p_sum.x, p_sum.y) # DEBUG
-# print("max :", p_max.x, p_max.y) # DEBUG
-# print("----------------------") # DEBUG
-#######################################################
- sv_nb = sv_nb // self.__turns
+ sv_nb = len(stroke) // self.__turns
center = (p_min + p_max) / 2
radius = (center.x - p_min.x + center.y - p_min.y) / 2
- p_new = Vector((0.0, 0.0))
-#######################################################
R = self.__random_radius
C = self.__random_center
- i = 0
- it = stroke.stroke_vertices_begin()
+
+ # The directions (and phases) are calculated using a seperate
+ # function decorated with an lru-cache. This guarantees that
+ # the directions (involving sin and cos) are calculated as few
+ # times as possible.
+ #
+ # This works because the phases and directions are only
+ # dependant on the stroke length, and the chance that
+ # stroke.resample() above produces strokes of the same length
+ # is quite high.
+ #
+ # In tests, the amount of calls to sin() and cos() went from
+ # over 21000 to just 32 times, yielding a speedup of over 100%
+ directions = phase_to_direction(sv_nb)
+
+ it = iter(stroke)
+
for j in range(self.__turns):
prev_radius = radius
prev_center = center
- radius = radius + randint(-R, R)
- center = center + Vector((randint(-C, C), randint(-C, C)))
- while i < sv_nb and not it.is_end:
- t = float(i) / float(sv_nb - 1)
- r = prev_radius + (radius - prev_radius) * t
- c = prev_center + (center - prev_center) * t
- p_new.x = c.x + r * cos(2 * pi * t)
- p_new.y = c.y + r * sin(2 * pi * t)
- it.object.point = p_new
- i = i + 1
- it.increment()
- i = 1
- verticesToRemove = []
- while not it.is_end:
- verticesToRemove.append(it.object)
+ radius += randint(-R, R)
+ center += Vector((randint(-C, C), randint(-C, C)))
+
+ for (phase, direction), svert in zip(directions, it):
+ r = prev_radius + (radius - prev_radius) * phase
+ c = prev_center + (center - prev_center) * phase
+ svert.point = c + r * direction
+
+ if not it.is_end:
it.increment()
- for sv in verticesToRemove:
- stroke.remove_vertex(sv)
+ for sv in tuple(it):
+ stroke.remove_vertex(sv)
+
stroke.update_length()
class pyBluePrintEllipsesShader(StrokeShader):
- """
- Draws the silhouette of the object as an ellips
- """
def __init__(self, turns=1, random_radius=3, random_center=5):
StrokeShader.__init__(self)
self.__turns = turns
@@ -1140,311 +861,300 @@ class pyBluePrintEllipsesShader(StrokeShader):
self.__random_radius = random_radius
def shade(self, stroke):
- it = stroke.stroke_vertices_begin()
- if it.is_end:
- return
- p_min = it.object.point.copy()
- p_max = it.object.point.copy()
- while not it.is_end:
- p = it.object.point
- if p.x < p_min.x:
- p_min.x = p.x
- if p.x > p_max.x:
- p_max.x = p.x
- if p.y < p_min.y:
- p_min.y = p.y
- if p.y > p_max.y:
- p_max.y = p.y
- it.increment()
+ p_min, p_max = bounding_box(stroke)
+
stroke.resample(32 * self.__turns)
- sv_nb = stroke.stroke_vertices_size()
- sv_nb = sv_nb // self.__turns
+ sv_nb = len(stroke) // self.__turns
+
center = (p_min + p_max) / 2
radius = center - p_min
- p_new = Vector((0.0, 0.0))
-#######################################################
+
R = self.__random_radius
C = self.__random_center
- i = 0
- it = stroke.stroke_vertices_begin()
+
+ # for description of the line below, see pyBluePrintCirclesShader
+ directions = phase_to_direction(sv_nb)
+ it = iter(stroke)
for j in range(self.__turns):
prev_radius = radius
prev_center = center
radius = radius + Vector((randint(-R, R), randint(-R, R)))
center = center + Vector((randint(-C, C), randint(-C, C)))
- while i < sv_nb and not it.is_end:
- t = float(i) / float(sv_nb - 1)
- r = prev_radius + (radius - prev_radius) * t
- c = prev_center + (center - prev_center) * t
- p_new.x = c.x + r.x * cos(2 * pi * t)
- p_new.y = c.y + r.y * sin(2 * pi * t)
- it.object.point = p_new
- i = i + 1
- it.increment()
- i = 1
- verticesToRemove = []
- while not it.is_end:
- verticesToRemove.append(it.object)
+
+ for (phase, direction), svert in zip(directions, it):
+ r = prev_radius + (radius - prev_radius) * phase
+ c = prev_center + (center - prev_center) * phase
+ svert.point = (c.x + r.x * direction.x, c.y + r.y * direction.y)
+
+ # remove exessive vertices
+ if not it.is_end:
it.increment()
- for sv in verticesToRemove:
- stroke.remove_vertex(sv)
+ for sv in tuple(it):
+ stroke.remove_vertex(sv)
+
stroke.update_length()
class pyBluePrintSquaresShader(StrokeShader):
- """
- Draws the silhouette of the object as a square
- """
def __init__(self, turns=1, bb_len=10, bb_rand=0):
StrokeShader.__init__(self)
- self.__turns = turns
+ self.__turns = turns # does not have any effect atm
self.__bb_len = bb_len
self.__bb_rand = bb_rand
def shade(self, stroke):
- it = stroke.stroke_vertices_begin()
- if it.is_end:
+ # this condition will lead to errors later, end now
+ if len(stroke) < 1:
return
- p_min = it.object.point.copy()
- p_max = it.object.point.copy()
- while not it.is_end:
- p = it.object.point
- if p.x < p_min.x:
- p_min.x = p.x
- if p.x > p_max.x:
- p_max.x = p.x
- if p.y < p_min.y:
- p_min.y = p.y
- if p.y > p_max.y:
- p_max.y = p.y
- it.increment()
+
+ # get minimum and maximum coordinates
+ p_min, p_max = bounding_box(stroke)
+
stroke.resample(32 * self.__turns)
- sv_nb = stroke.stroke_vertices_size()
-#######################################################
- sv_nb = sv_nb // self.__turns
- first = sv_nb // 4
- second = 2 * first
- third = 3 * first
- fourth = sv_nb
- p_first = Vector((p_min.x - self.__bb_len, p_min.y))
- p_first_end = Vector((p_max.x + self.__bb_len, p_min.y))
- p_second = Vector((p_max.x, p_min.y - self.__bb_len))
- p_second_end = Vector((p_max.x, p_max.y + self.__bb_len))
- p_third = Vector((p_max.x + self.__bb_len, p_max.y))
- p_third_end = Vector((p_min.x - self.__bb_len, p_max.y))
- p_fourth = Vector((p_min.x, p_max.y + self.__bb_len))
- p_fourth_end = Vector((p_min.x, p_min.y - self.__bb_len))
-#######################################################
- R = self.__bb_rand
- r = self.__bb_rand // 2
- it = stroke.stroke_vertices_begin()
- visible = True
+ num_segments = len(stroke) // self.__turns
+ f = num_segments // 4
+ # indices of the vertices that will form corners
+ first, second, third, fourth = (f, f * 2, f * 3, num_segments)
+
+ # construct points of the backbone
+ bb_len = self.__bb_len
+ points = (
+ Vector((p_min.x - bb_len, p_min.y)),
+ Vector((p_max.x + bb_len, p_min.y)),
+ Vector((p_max.x, p_min.y - bb_len)),
+ Vector((p_max.x, p_max.y + bb_len)),
+ Vector((p_max.x + bb_len, p_max.y)),
+ Vector((p_min.x - bb_len, p_max.y)),
+ Vector((p_min.x, p_max.y + bb_len)),
+ Vector((p_min.x, p_min.y - bb_len)),
+ )
+
+ # add randomization to the points (if needed)
+ if self.__bb_rand:
+ R, r = self.__bb_rand, self.__bb_rand // 2
+
+ randomization_mat = (
+ Vector((randint(-R, R), randint(-r, r))),
+ Vector((randint(-R, R), randint(-r, r))),
+ Vector((randint(-r, r), randint(-R, R))),
+ Vector((randint(-r, r), randint(-R, R))),
+ Vector((randint(-R, R), randint(-r, r))),
+ Vector((randint(-R, R), randint(-r, r))),
+ Vector((randint(-r, r), randint(-R, R))),
+ Vector((randint(-r, r), randint(-R, R))),
+ )
+
+ # combine both tuples
+ points = tuple(p + rand for (p, rand) in zip(points, randomization_mat))
+
+
+ # substract even from uneven; result is length four tuple of vectors
+ it = iter(points)
+ old_vecs = tuple(next(it) - current for current in it)
+
+ it = iter(stroke)
+ verticesToRemove = list()
for j in range(self.__turns):
- p_first = p_first + Vector((randint(-R, R), randint(-r, r)))
- p_first_end = p_first_end + Vector((randint(-R, R), randint(-r, r)))
- p_second = p_second + Vector((randint(-r, r), randint(-R, R)))
- p_second_end = p_second_end + Vector((randint(-r, r), randint(-R, R)))
- p_third = p_third + Vector((randint(-R, R), randint(-r, r)))
- p_third_end = p_third_end + Vector((randint(-R, R), randint(-r, r)))
- p_fourth = p_fourth + Vector((randint(-r, r), randint(-R, R)))
- p_fourth_end = p_fourth_end + Vector((randint(-r, r), randint(-R, R)))
- vec_first = p_first_end - p_first
- vec_second = p_second_end - p_second
- vec_third = p_third_end - p_third
- vec_fourth = p_fourth_end - p_fourth
- i = 0
- while i < sv_nb and not it.is_end:
+ for i, svert in zip(range(num_segments), it):
if i < first:
- p_new = p_first + vec_first * float(i)/float(first - 1)
- if i == first - 1:
- visible = False
+ svert.point = points[0] + old_vecs[0] * i / (first - 1)
+ svert.attribute.visible = (i != first - 1)
elif i < second:
- p_new = p_second + vec_second * float(i - first)/float(second - first - 1)
- if i == second - 1:
- visible = False
+ svert.point = points[2] + old_vecs[1] * (i - first) / (second - first - 1)
+ svert.attribute.visible = (i != second - 1)
elif i < third:
- p_new = p_third + vec_third * float(i - second)/float(third - second - 1)
- if i == third - 1:
- visible = False
+ svert.point = points[4] + old_vecs[2] * (i - second) / (third - second - 1)
+ svert.attribute.visible = (i != third - 1)
+ elif i < fourth:
+ svert.point = points[6] + old_vecs[3] * (i - third) / (fourth - third - 1)
+ svert.attribute.visible = (i != fourth - 1)
else:
- p_new = p_fourth + vec_fourth * float(i - third)/float(fourth - third - 1)
- if i == fourth - 1:
- visible = False
- if it.object is None:
- i = i + 1
- it.increment()
- if not visible:
- visible = True
- continue
- it.object.point = p_new
- it.object.attribute.visible = visible
- if not visible:
- visible = True
- i = i + 1
- it.increment()
- verticesToRemove = []
- while not it.is_end:
- verticesToRemove.append(it.object)
+ # special case; remove these vertices
+ verticesToRemove.append(svert)
+
+ # remove exessive vertices (if any)
+ if not it.is_end:
it.increment()
- for sv in verticesToRemove:
- stroke.remove_vertex(sv)
+ verticesToRemove += [svert for svert in it]
+ for sv in verticesToRemove:
+ stroke.remove_vertex(sv)
stroke.update_length()
-# needs a docstring
class pyBluePrintDirectedSquaresShader(StrokeShader):
+ """
+ Replaces the stroke with a directed square
+ """
def __init__(self, turns=1, bb_len=10, mult=1):
StrokeShader.__init__(self)
self.__mult = mult
self.__turns = turns
- self.__bb_len = 1 + float(bb_len) / 100
+ self.__bb_len = 1 + bb_len * 0.01
def shade(self, stroke):
stroke.resample(32 * self.__turns)
- p_mean = Vector((0.0, 0.0))
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- p = it.object.point
- p_mean = p_mean + p
- it.increment()
- sv_nb = stroke.stroke_vertices_size()
- p_mean = p_mean / sv_nb
- p_var_xx = 0
- p_var_yy = 0
- p_var_xy = 0
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- p = it.object.point
- p_var_xx = p_var_xx + pow(p.x - p_mean.x, 2)
- p_var_yy = p_var_yy + pow(p.y - p_mean.y, 2)
- p_var_xy = p_var_xy + (p.x - p_mean.x) * (p.y - p_mean.y)
- it.increment()
- p_var_xx = p_var_xx / sv_nb
- p_var_yy = p_var_yy / sv_nb
- p_var_xy = p_var_xy / sv_nb
-## print(p_var_xx, p_var_yy, p_var_xy)
- trace = p_var_xx + p_var_yy
- det = p_var_xx * p_var_yy - p_var_xy * p_var_xy
+ n = len(stroke)
+
+ p_mean = (1 / n) * sum((svert.point for svert in stroke), Vector((0.0, 0.0)))
+ p_var = Vector((0, 0))
+ p_var_xy = 0.0
+ for d in (svert.point - p_mean for svert in stroke):
+ p_var += Vector((d.x ** 2, d.y ** 2))
+ p_var_xy += d.x * d.y
+
+ # divide by number of vertices
+ p_var /= n
+ p_var_xy /= n
+ trace = p_var.x + p_var.y
+ det = p_var.x * p_var.y - pow(p_var_xy, 2)
+
sqrt_coeff = sqrt(trace * trace - 4 * det)
- lambda1 = (trace + sqrt_coeff) / 2
- lambda2 = (trace - sqrt_coeff) / 2
-## print(lambda1, lambda2)
- theta = atan(2 * p_var_xy / (p_var_xx - p_var_yy)) / 2
-## print(theta)
- if p_var_yy > p_var_xx:
+ lambda1, lambda2 = (trace + sqrt_coeff) / 2, (trace - sqrt_coeff) / 2
+ # make sure those numers aren't to small, if they are, rooting them will yield complex numbers
+ lambda1, lambda2 = max(1e-12, lambda1), max(1e-12, lambda2)
+ theta = atan(2 * p_var_xy / (p_var.x - p_var.y)) / 2
+
+ if p_var.y > p_var.x:
e1 = Vector((cos(theta + pi / 2), sin(theta + pi / 2))) * sqrt(lambda1) * self.__mult
- e2 = Vector((cos(theta + pi), sin(theta + pi))) * sqrt(lambda2) * self.__mult
+ e2 = Vector((cos(theta + pi ), sin(theta + pi ))) * sqrt(lambda2) * self.__mult
else:
- e1 = Vector((cos(theta), sin(theta))) * sqrt(lambda1) * self.__mult
+ e1 = Vector((cos(theta), sin(theta))) * sqrt(lambda1) * self.__mult
e2 = Vector((cos(theta + pi / 2), sin(theta + pi / 2))) * sqrt(lambda2) * self.__mult
-#######################################################
- sv_nb = sv_nb // self.__turns
- first = sv_nb // 4
- second = 2 * first
- third = 3 * first
- fourth = sv_nb
+
+ # partition the stroke
+ num_segments = len(stroke) // self.__turns
+ f = num_segments // 4
+ # indices of the vertices that will form corners
+ first, second, third, fourth = (f, f * 2, f * 3, num_segments)
+
bb_len1 = self.__bb_len
bb_len2 = 1 + (bb_len1 - 1) * sqrt(lambda1 / lambda2)
- p_first = p_mean - e1 - e2 * bb_len2
- p_second = p_mean - e1 * bb_len1 + e2
- p_third = p_mean + e1 + e2 * bb_len2
- p_fourth = p_mean + e1 * bb_len1 - e2
- vec_first = e2 * bb_len2 * 2
- vec_second = e1 * bb_len1 * 2
- vec_third = vec_first * -1
- vec_fourth = vec_second * -1
-#######################################################
- it = stroke.stroke_vertices_begin()
- visible = True
+ points = (
+ p_mean - e1 - e2 * bb_len2,
+ p_mean - e1 * bb_len1 + e2,
+ p_mean + e1 + e2 * bb_len2,
+ p_mean + e1 * bb_len1 - e2,
+ )
+
+ old_vecs = (
+ e2 * bb_len2 * 2,
+ e1 * bb_len1 * 2,
+ -e2 * bb_len2 * 2,
+ -e1 * bb_len1 * 2,
+ )
+
+ it = iter(stroke)
+ verticesToRemove = list()
for j in range(self.__turns):
- i = 0
- while i < sv_nb:
+ for i, svert in zip(range(num_segments), it):
if i < first:
- p_new = p_first + vec_first * float(i)/float(first - 1)
- if i == first - 1:
- visible = False
+ svert.point = points[0] + old_vecs[0] * i / (first - 1)
+ svert.attribute.visible = (i != first - 1)
elif i < second:
- p_new = p_second + vec_second * float(i - first)/float(second - first - 1)
- if i == second - 1:
- visible = False
+ svert.point = points[1] + old_vecs[1] * (i - first) / (second - first - 1)
+ svert.attribute.visible = (i != second - 1)
elif i < third:
- p_new = p_third + vec_third * float(i - second)/float(third - second - 1)
- if i == third - 1:
- visible = False
+ svert.point = points[2] + old_vecs[2] * (i - second) / (third - second - 1)
+ svert.attribute.visible = (i != third - 1)
+ elif i < fourth:
+ svert.point = points[3] + old_vecs[3] * (i - third) / (fourth - third - 1)
+ svert.attribute.visible = (i != fourth - 1)
else:
- p_new = p_fourth + vec_fourth * float(i - third)/float(fourth - third - 1)
- if i == fourth - 1:
- visible = False
- it.object.point = p_new
- it.object.attribute.visible = visible
- if not visible:
- visible = True
- i = i + 1
- it.increment()
- verticesToRemove = []
- while not it.is_end:
- verticesToRemove.append(it.object)
+ # special case; remove these vertices
+ verticesToRemove.append(svert)
+
+ # remove exessive vertices
+ if not it.is_end:
it.increment()
- for sv in verticesToRemove:
- stroke.remove_vertex(sv)
+ verticesToRemove += [svert for svert in it]
+ for sv in verticesToRemove:
+ stroke.remove_vertex(sv)
stroke.update_length()
-class pyModulateAlphaShader(StrokeShader):
- """
- Limits the stroke's alpha between a min and max value
- """
- def __init__(self, min=0, max=1):
- StrokeShader.__init__(self)
- self.__min = min
- self.__max = max
+# -- various (used in the parameter editor) -- #
- def shade(self, stroke):
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- alpha = it.object.attribute.alpha
- p = it.object.point
- alpha = alpha * p.y / 400
- if alpha < self.__min:
- alpha = self.__min
- elif alpha > self.__max:
- alpha = self.__max
- it.object.attribute.alpha = alpha
- it.increment()
+class RoundCapShader(StrokeShader):
+ def round_cap_thickness(self, x):
+ x = max(0.0, min(x, 1.0))
+ return pow(1.0 - (x ** 2.0), 0.5)
-## various
-class pyDummyShader(StrokeShader):
def shade(self, stroke):
- it = stroke.stroke_vertices_begin()
- while not it.is_end:
- toto = Interface0DIterator(it)
- att = it.object.attribute
- att.color = (0.3, 0.4, 0.4)
- att.thickness = (0, 5)
- it.increment()
+ # save the location and attribute of stroke vertices
+ buffer = tuple((Vector(sv.point), StrokeAttribute(sv.attribute)) for sv in stroke)
+ nverts = len(buffer)
+ if nverts < 2:
+ return
+ # calculate the number of additional vertices to form caps
+ thickness_beg = sum(stroke[0].attribute.thickness)
+ caplen_beg = thickness_beg / 2.0
+ nverts_beg = max(5, int(thickness_beg))
+
+ thickness_end = sum(stroke[-1].attribute.thickness)
+ caplen_end = (thickness_end) / 2.0
+ nverts_end = max(5, int(thickness_end))
+
+ # adjust the total number of stroke vertices
+ stroke.resample(nverts + nverts_beg + nverts_end)
+ # restore the location and attribute of the original vertices
+ for i, (p, attr) in enumerate(buffer):
+ stroke[nverts_beg + i].point = p
+ stroke[nverts_beg + i].attribute = attr
+ # reshape the cap at the beginning of the stroke
+ q, attr = buffer[1]
+ p, attr = buffer[0]
+ direction = (p - q).normalized() * caplen_beg
+ n = 1.0 / nverts_beg
+ R, L = attr.thickness
+ for t, svert in zip(range(nverts_beg, 0, -1), stroke):
+ r = self.round_cap_thickness((t + 1) * n)
+ svert.point = p + direction * t * n
+ svert.attribute = attr
+ svert.attribute.thickness = (R * r, L * r)
+ # reshape the cap at the end of the stroke
+ q, attr = buffer[-2]
+ p, attr = buffer[-1]
+ direction = (p - q).normalized() * caplen_beg
+ n = 1.0 / nverts_end
+ R, L = attr.thickness
+ for t, svert in zip(range(nverts_end, 0, -1), reversed(stroke)):
+ r = self.round_cap_thickness((t + 1) * n)
+ svert.point = p + direction * t * n
+ svert.attribute = attr
+ svert.attribute.thickness = (R * r, L * r)
+ # update the curvilinear 2D length of each vertex
+ stroke.update_length()
-class pyDebugShader(StrokeShader):
+class SquareCapShader(StrokeShader):
def shade(self, stroke):
- fe = CF.get_selected_fedge()
- id1 = fe.first_svertex.id
- id2 = fe.second_svertex.id
- #print(id1.first, id1.second)
- #print(id2.first, id2.second)
- it = stroke.stroke_vertices_begin()
- found = True
- foundfirst = True
- foundsecond = False
- while not it.is_end:
- cp = it.object
- if cp.first_svertex.id == id1 or cp.second_svertex.id == id1:
- foundfirst = True
- if cp.first_svertex.id == id2 or cp.second_svertex.id == id2:
- foundsecond = True
- if foundfirst and foundsecond:
- found = True
- break
- it.increment()
- if found:
- print("The selected Stroke id is: ", stroke.id.first, stroke.id.second)
+ # save the location and attribute of stroke vertices
+ buffer = tuple((Vector(sv.point), StrokeAttribute(sv.attribute)) for sv in stroke)
+ nverts = len(buffer)
+ if nverts < 2:
+ return
+ # calculate the number of additional vertices to form caps
+ caplen_beg = sum(stroke[0].attribute.thickness) / 2.0
+ nverts_beg = 1
+
+ caplen_end = sum(stroke[-1].attribute.thickness) / 2.0
+ nverts_end = 1
+ # adjust the total number of stroke vertices
+ stroke.resample(nverts + nverts_beg + nverts_end)
+ # restore the location and attribute of the original vertices
+ for i, (p, attr) in zip(range(nverts), buffer):
+ stroke[nverts_beg + i].point = p
+ stroke[nverts_beg + i].attribute = attr
+ # reshape the cap at the beginning of the stroke
+ q, attr = buffer[1]
+ p, attr = buffer[0]
+ stroke[0].point += (p - q).normalized() * caplen_beg
+ stroke[0].attribute = attr
+ # reshape the cap at the end of the stroke
+ q, attr = buffer[-2]
+ p, attr = buffer[-1]
+ stroke[-1].point += (p - q).normalized() * caplen_end
+ stroke[-1].attribute = attr
+ # update the curvilinear 2D length of each vertex
+ stroke.update_length()
diff --git a/release/scripts/freestyle/modules/freestyle/utils.py b/release/scripts/freestyle/modules/freestyle/utils.py
index 24ff86d5ef6..1b576791e9b 100644
--- a/release/scripts/freestyle/modules/freestyle/utils.py
+++ b/release/scripts/freestyle/modules/freestyle/utils.py
@@ -27,11 +27,275 @@ from _freestyle import (
integrate,
)
-# constructs for definition of helper functions in Python
-from freestyle.types import (
- StrokeVertexIterator,
- )
-import mathutils
+from mathutils import Vector
+from functools import lru_cache
+from math import cos, sin, pi
+
+
+# -- real utility functions -- #
+
+
+def rgb_to_bw(r, g, b):
+ """ Method to convert rgb to a bw intensity value. """
+ return 0.35 * r + 0.45 * g + 0.2 * b
+
+
+def bound(lower, x, higher):
+ """ Returns x bounded by a maximum and minimum value. equivalent to:
+ return min(max(x, lower), higher)
+ """
+ # this is about 50% quicker than min(max(x, lower), higher)
+ return (lower if x <= lower else higher if x >= higher else x)
+
+
+def bounding_box(stroke):
+ """
+ Returns the maximum and minimum coordinates (the bounding box) of the stroke's vertices
+ """
+ x, y = zip(*(svert.point for svert in stroke))
+ return (Vector((min(x), min(y))), Vector((max(x), max(y))))
+
+
+# -- General helper functions -- #
+
+
+@lru_cache(maxsize=32)
+def phase_to_direction(length):
+ """
+ Returns a list of tuples each containing:
+ - the phase
+ - a Vector with the values of the cosine and sine of 2pi * phase (the direction)
+ """
+ results = list()
+ for i in range(length):
+ phase = i / (length - 1)
+ results.append((phase, Vector((cos(2 * pi * phase), sin(2 * pi * phase)))))
+ return results
+
+
+# -- helper functions for chaining -- #
+
+
+def get_chain_length(ve, orientation):
+ """Returns the 2d length of a given ViewEdge """
+ from freestyle.chainingiterators import pyChainSilhouetteGenericIterator
+ length = 0.0
+ # setup iterator
+ _it = pyChainSilhouetteGenericIterator(False, False)
+ _it.begin = ve
+ _it.current_edge = ve
+ _it.orientation = orientation
+ _it.init()
+
+ # run iterator till end of chain
+ while not (_it.is_end):
+ length += _it.object.length_2d
+ if (_it.is_begin):
+ # _it has looped back to the beginning;
+ # break to prevent infinite loop
+ break
+ _it.increment()
+
+ # reset iterator
+ _it.begin = ve
+ _it.current_edge = ve
+ _it.orientation = orientation
+
+ # run iterator till begin of chain
+ if not _it.is_begin:
+ _it.decrement()
+ while not (_it.is_end or _it.is_begin):
+ length += _it.object.length_2d
+ _it.decrement()
+
+ return length
+
+
+def find_matching_vertex(id, it):
+ """Finds the matching vertexn, or returns None """
+ return next((ve for ve in it if ve.id == id), None)
+
+
+# -- helper functions for iterating -- #
+
+
+def iter_current_previous(stroke):
+ """
+ iterates over the given iterator. yields a tuple of the form
+ (it, prev, current)
+ """
+ prev = stroke[0]
+ it = Interface0DIterator(stroke)
+ for current in it:
+ yield (it, prev, current)
+
+
+def iter_t2d_along_stroke(stroke):
+ """
+ Yields the distance between two stroke vertices
+ relative to the total stroke length.
+ """
+ total = stroke.length_2d
+ distance = 0.0
+ for it, prev, svert in iter_current_previous(stroke):
+ distance += (prev.point - svert.point).length
+ t = min(distance / total, 1.0) if total > 0.0 else 0.0
+ yield (it, t)
+
+
+def iter_distance_from_camera(stroke, range_min, range_max):
+ """
+ Yields the distance to the camera relative to the maximum
+ possible distance for every stroke vertex, constrained by
+ given minimum and maximum values.
+ """
+ normfac = range_max - range_min # normalization factor
+ it = Interface0DIterator(stroke)
+ for svert in it:
+ distance = svert.point_3d.length # in the camera coordinate
+ if distance < range_min:
+ t = 0.0
+ elif distance > range_max:
+ t = 1.0
+ else:
+ t = (distance - range_min) / normfac
+ yield (it, t)
+
+
+def iter_distance_from_object(stroke, object, range_min, range_max):
+ """
+ yields the distance to the given object relative to the maximum
+ possible distance for every stroke vertex, constrained by
+ given minimum and maximum values.
+ """
+ scene = getCurrentScene()
+ mv = scene.camera.matrix_world.copy().inverted() # model-view matrix
+ loc = mv * object.location # loc in the camera coordinate
+ normfac = range_max - range_min # normalization factor
+ it = Interface0DIterator(stroke)
+ for svert in it:
+ distance = (svert.point_3d - loc).length # in the camera coordinate
+ if distance < range_min:
+ t = 0.0
+ elif distance > range_max:
+ t = 1.0
+ else:
+ t = (distance - range_min) / normfac
+ yield (it, t)
+
+
+def iter_material_color(stroke, material_attribute):
+ """
+ yields the specified material attribute for every stroke vertex.
+ the material is taken from the object behind the vertex.
+ """
+ func = CurveMaterialF0D()
+ it = Interface0DIterator(stroke)
+ for inter in it:
+ material = func(it)
+ if material_attribute == 'DIFF':
+ color = material.diffuse[0:3]
+ elif material_attribute == 'SPEC':
+ color = material.specular[0:3]
+ else:
+ raise ValueError("unexpected material attribute: " + material_attribute)
+ yield (it, color)
+
+
+def iter_material_value(stroke, material_attribute):
+ """
+ yields a specific material attribute
+ from the vertex' underlying material.
+ """
+ func = CurveMaterialF0D()
+ it = Interface0DIterator(stroke)
+ for svert in it:
+ material = func(it)
+ if material_attribute == 'DIFF':
+ t = rgb_to_bw(*material.diffuse[0:3])
+ elif material_attribute == 'DIFF_R':
+ t = material.diffuse[0]
+ elif material_attribute == 'DIFF_G':
+ t = material.diffuse[1]
+ elif material_attribute == 'DIFF_B':
+ t = material.diffuse[2]
+ elif material_attribute == 'SPEC':
+ t = rgb_to_bw(*material.specular[0:3])
+ elif material_attribute == 'SPEC_R':
+ t = material.specular[0]
+ elif material_attribute == 'SPEC_G':
+ t = material.specular[1]
+ elif material_attribute == 'SPEC_B':
+ t = material.specular[2]
+ elif material_attribute == 'SPEC_HARDNESS':
+ t = material.shininess
+ elif material_attribute == 'ALPHA':
+ t = material.diffuse[3]
+ else:
+ raise ValueError("unexpected material attribute: " + material_attribute)
+ yield (it, t)
+
+
+def iter_distance_along_stroke(stroke):
+ """
+ yields the absolute distance between
+ the current and preceding vertex.
+ """
+ distance = 0.0
+ prev = stroke[0]
+ it = Interface0DIterator(stroke)
+ for svert in it:
+ p = svert.point
+ distance += (prev - p).length
+ prev = p.copy() # need a copy because the point can be altered
+ yield it, distance
+
+
+def iter_triplet(it):
+ """
+ Iterates over it, yielding a tuple containing
+ the current vertex and its immediate neighbors
+ """
+ prev = next(it)
+ current = next(it)
+ for succ in it:
+ yield prev, current, succ
+ prev, current = current, succ
+
+
+# -- mathmatical operations -- #
+
+
+def stroke_curvature(it):
+ """
+ Compute the 2D curvature at the stroke vertex pointed by the iterator 'it'.
+ K = 1 / R
+ where R is the radius of the circle going through the current vertex and its neighbors
+ """
+
+ if it.is_end or it.is_begin:
+ return 0.0
+
+ next = it.incremented().point
+ prev = it.decremented().point
+ current = it.object.point
+
+
+ ab = (current - prev)
+ bc = (next - current)
+ ac = (prev - next)
+
+ a, b, c = ab.length, bc.length, ac.length
+
+ try:
+ area = 0.5 * ab.cross(ac)
+ K = (4 * area) / (a * b * c)
+ K = bound(0.0, K, 1.0)
+
+ except ZeroDivisionError:
+ K = 0.0
+
+ return K
def stroke_normal(it):
@@ -42,28 +306,21 @@ def stroke_normal(it):
they have already been modified by stroke geometry modifiers.
"""
# first stroke segment
- it_next = StrokeVertexIterator(it)
- it_next.increment()
+ it_next = it.incremented()
if it.is_begin:
e = it_next.object.point_2d - it.object.point_2d
- n = mathutils.Vector((e[1], -e[0]))
- n.normalize()
- return n
+ n = Vector((e[1], -e[0]))
+ return n.normalized()
# last stroke segment
- it_prev = StrokeVertexIterator(it)
- it_prev.decrement()
+ it_prev = it.decremented()
if it_next.is_end:
e = it.object.point_2d - it_prev.object.point_2d
- n = mathutils.Vector((e[1], -e[0]))
- n.normalize()
- return n
+ n = Vector((e[1], -e[0]))
+ return n.normalized()
# two subsequent stroke segments
e1 = it_next.object.point_2d - it.object.point_2d
e2 = it.object.point_2d - it_prev.object.point_2d
- n1 = mathutils.Vector((e1[1], -e1[0]))
- n2 = mathutils.Vector((e2[1], -e2[0]))
- n1.normalize()
- n2.normalize()
- n = n1 + n2
- n.normalize()
- return n
+ n1 = Vector((e1[1], -e1[0])).normalized()
+ n2 = Vector((e2[1], -e2[0])).normalized()
+ n = (n1 + n2)
+ return n.normalized()
diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py
index aa10b71783c..9371159e0e7 100644
--- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py
+++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py
@@ -43,6 +43,7 @@ class SpellChecker():
"fader",
"globbing",
"hasn", # hasn't
+ "hetero",
"hoc", # ad-hoc
"indices",
"iridas",
@@ -52,6 +53,7 @@ class SpellChecker():
"latin",
"merchantability",
"mplayer",
+ "pong", # ping pong
"teleport", "teleporting",
"vertices",
@@ -89,8 +91,10 @@ class SpellChecker():
"customdata",
"dataset", "datasets",
"de",
+ "deconstruct",
"defocus",
"denoise",
+ "deselect", "deselecting", "deselection",
"despill", "despilling",
"editcurve",
"editmesh",
@@ -123,8 +127,8 @@ class SpellChecker():
"multiuser",
"namespace",
"keyconfig",
+ "online",
"playhead",
- "polyline",
"popup", "popups",
"pre",
"precache", "precaching",
@@ -223,6 +227,9 @@ class SpellChecker():
"rasterized", "rasterization", "rasterizer",
"renderer", "renderable", "renderability",
+ # Really bad!!!
+ "convertor",
+
# Abbreviations
"aero",
"amb",
@@ -233,6 +240,7 @@ class SpellChecker():
"const",
"coord", "coords",
"degr",
+ "diff",
"dof",
"dupli", "duplis",
"eg",
@@ -279,6 +287,7 @@ class SpellChecker():
"vel", # velocity!
"vert", "verts",
"vis",
+ "xor",
"xyz", "xzy", "yxz", "yzx", "zxy", "zyx",
"xy", "xz", "yx", "yz", "zx", "zy",
@@ -290,6 +299,7 @@ class SpellChecker():
"fribidi",
"gettext",
"hashable",
+ "hotspot",
"intrinsics",
"isosurface",
"jitter", "jittering", "jittered",
@@ -302,8 +312,11 @@ class SpellChecker():
"normals",
"numpad",
"octree",
+ "omnidirectional",
"opengl",
"openmp",
+ "photoreceptor",
+ "poly",
"polyline", "polylines",
"pulldown", "pulldowns",
"quantized",
diff --git a/release/scripts/modules/bpy/utils.py b/release/scripts/modules/bpy/utils.py
index ce1efa43b3e..5621af29bc3 100644
--- a/release/scripts/modules/bpy/utils.py
+++ b/release/scripts/modules/bpy/utils.py
@@ -44,6 +44,7 @@ __all__ = (
"script_paths",
"smpte_from_frame",
"smpte_from_seconds",
+ "units",
"unregister_class",
"unregister_module",
"user_resource",
@@ -58,6 +59,7 @@ from _bpy import (
)
from _bpy import script_paths as _bpy_script_paths
from _bpy import user_resource as _user_resource
+from _bpy import _utils_units as units
import bpy as _bpy
import os as _os
diff --git a/release/scripts/modules/nodeitems_utils.py b/release/scripts/modules/nodeitems_utils.py
index f017c76ae6f..1fef6c3a019 100644
--- a/release/scripts/modules/nodeitems_utils.py
+++ b/release/scripts/modules/nodeitems_utils.py
@@ -108,7 +108,7 @@ def register_node_categories(identifier, cat_list):
"bl_space_type": 'NODE_EDITOR',
"bl_region_type": 'TOOLS',
"bl_label": cat.name,
- "bl_options": {'DEFAULT_CLOSED'},
+ "bl_category": cat.name,
"category": cat,
"poll": cat.poll,
"draw": draw_node_item,
diff --git a/release/scripts/presets/keyconfig/maya.py b/release/scripts/presets/keyconfig/maya.py
index 42c25025757..da10b17422e 100644
--- a/release/scripts/presets/keyconfig/maya.py
+++ b/release/scripts/presets/keyconfig/maya.py
@@ -1448,9 +1448,9 @@ kmi.properties.value = 8
# Map Node Editor
km = kc.keymaps.new('Node Editor', space_type='NODE_EDITOR', region_type='WINDOW', modal=False)
-kmi = km.keymap_items.new('node.select', 'SELECTMOUSE', 'CLICK')
+kmi = km.keymap_items.new('node.select', 'SELECTMOUSE', 'PRESS')
kmi.properties.extend = False
-kmi = km.keymap_items.new('node.select', 'SELECTMOUSE', 'CLICK', shift=True)
+kmi = km.keymap_items.new('node.select', 'SELECTMOUSE', 'PRESS', shift=True)
kmi.properties.extend = True
kmi = km.keymap_items.new('node.select_border', 'EVT_TWEAK_S', 'ANY')
kmi.properties.extend = False
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index 86ae8fdc4e6..4281c908afd 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -802,13 +802,14 @@ class WM_OT_path_open(Operator):
if sys.platform[:3] == "win":
os.startfile(filepath)
elif sys.platform == "darwin":
- subprocess.Popen(["open", filepath])
+ subprocess.check_call(["open", filepath])
else:
try:
- subprocess.Popen(["xdg-open", filepath])
- except OSError:
+ subprocess.check_call(["xdg-open", filepath])
+ except:
# xdg-open *should* be supported by recent Gnome, KDE, Xfce
- pass
+ import traceback
+ traceback.print_exc()
return {'FINISHED'}
diff --git a/release/scripts/startup/bl_ui/properties_texture.py b/release/scripts/startup/bl_ui/properties_texture.py
index ec91be74a36..626cdaa8b1b 100644
--- a/release/scripts/startup/bl_ui/properties_texture.py
+++ b/release/scripts/startup/bl_ui/properties_texture.py
@@ -227,7 +227,7 @@ class TEXTURE_PT_context_texture(TextureButtonsPanel, Panel):
class TEXTURE_PT_preview(TextureButtonsPanel, Panel):
bl_label = "Preview"
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME', 'CYCLES'}
def draw(self, context):
layout = self.layout
diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py
index 31881671fda..ddb83310fe8 100644
--- a/release/scripts/startup/bl_ui/space_info.py
+++ b/release/scripts/startup/bl_ui/space_info.py
@@ -281,7 +281,7 @@ class INFO_MT_help(Menu):
layout = self.layout
layout.operator("wm.url_open", text="Manual", icon='HELP').url = "http://wiki.blender.org/index.php/Doc:2.6/Manual"
- layout.operator("wm.url_open", text="Release Log", icon='URL').url = "http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.70"
+ layout.operator("wm.url_open", text="Release Log", icon='URL').url = "http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/%d.%d" % bpy.app.version[:2]
layout.separator()
layout.operator("wm.url_open", text="Blender Website", icon='URL').url = "http://www.blender.org"
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index bc478ed8713..31ca972726e 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -487,8 +487,10 @@ class USERPREF_PT_system(Panel):
sub.active = system.use_weight_color_range
sub.template_color_ramp(system, "weight_color_range", expand=True)
+ column.separator()
+ column.prop(system, "font_path_ui")
+
if bpy.app.build_options.international:
- column.separator()
column.prop(system, "use_international_fonts")
if system.use_international_fonts:
column.prop(system, "language")
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 3db489eb305..6f0b97fcb4b 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -477,7 +477,7 @@ class VIEW3D_MT_view_align(Menu):
class VIEW3D_MT_view_align_selected(Menu):
- bl_label = "Align View to Selected"
+ bl_label = "Align View to Active"
def draw(self, context):
layout = self.layout
@@ -2160,6 +2160,7 @@ class VIEW3D_MT_edit_mesh_vertices(Menu):
layout.operator("mesh.merge")
layout.operator("mesh.rip_move")
layout.operator("mesh.rip_move_fill")
+ layout.operator("mesh.rip_edge_move")
layout.operator("mesh.split")
layout.operator_menu_enum("mesh.separate", "type")
layout.operator("mesh.vert_connect", text="Connect")
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index c20c36bf454..0f2c04d1cdc 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -363,6 +363,30 @@ class VIEW3D_PT_tools_meshedit(View3DPanel, Panel):
draw_repeat_tools(context, layout)
+class VIEW3D_PT_tools_meshweight(View3DPanel, Panel):
+ bl_category = "Tools"
+ bl_context = "mesh_edit"
+ bl_label = "Weight Tools"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ # Used for Weight-Paint mode and Edit-Mode
+ @staticmethod
+ def draw_generic(layout):
+ col = layout.column()
+ col.operator("object.vertex_group_normalize_all", text="Normalize All")
+ col.operator("object.vertex_group_normalize", text="Normalize")
+ col.operator("object.vertex_group_mirror", text="Mirror")
+ col.operator("object.vertex_group_invert", text="Invert")
+ col.operator("object.vertex_group_clean", text="Clean")
+ col.operator("object.vertex_group_quantize", text="Quantize")
+ col.operator("object.vertex_group_levels", text="Levels")
+ col.operator("object.vertex_group_blend", text="Blend")
+ col.operator("object.vertex_group_limit_total", text="Limit Total")
+ col.operator("object.vertex_group_fix", text="Fix Deforms")
+
+ def draw(self, context):
+ layout = self.layout
+ self.draw_generic(layout)
class VIEW3D_PT_tools_add_mesh_edit(View3DPanel, Panel):
bl_category = "Create"
@@ -1273,7 +1297,7 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel):
col = layout.column()
col.active = context.sculpt_object.use_dynamic_topology_sculpting
sub = col.column(align=True)
- sub.active = brush and brush.sculpt_tool not in ('MASK')
+ sub.active = (brush and brush.sculpt_tool != 'MASK')
if (sculpt.detail_type_method == 'CONSTANT'):
row = sub.row(align=True)
row.operator("sculpt.sample_detail_size", text="", icon='EYEDROPPER')
@@ -1411,20 +1435,11 @@ class VIEW3D_PT_tools_weightpaint(View3DPanel, Panel):
def draw(self, context):
layout = self.layout
+ VIEW3D_PT_tools_meshweight.draw_generic(layout)
col = layout.column()
- col.operator("object.vertex_group_normalize_all", text="Normalize All")
- col.operator("object.vertex_group_normalize", text="Normalize")
- col.operator("object.vertex_group_mirror", text="Mirror")
- col.operator("object.vertex_group_invert", text="Invert")
- col.operator("object.vertex_group_clean", text="Clean")
- col.operator("object.vertex_group_quantize", text="Quantize")
- col.operator("object.vertex_group_levels", text="Levels")
- col.operator("object.vertex_group_blend", text="Blend")
- col.operator("object.vertex_group_transfer_weight", text="Transfer Weights")
- col.operator("object.vertex_group_limit_total", text="Limit Total")
- col.operator("object.vertex_group_fix", text="Fix Deforms")
col.operator("paint.weight_gradient")
+ col.operator("object.vertex_group_transfer_weight", text="Transfer Weights")
class VIEW3D_PT_tools_weightpaint_options(Panel, View3DPaintPanel):
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 34c0739d50c..0a90b9f01f8 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -50,9 +50,10 @@ class TextureNodeCategory(NodeCategory):
return context.space_data.tree_type == 'TextureNodeTree'
-# menu entry for making a new group from selected nodes
-def group_make_draw(self, layout, context):
+# menu entry for node group tools
+def group_tools_draw(self, layout, context):
layout.operator("node.group_make")
+ layout.operator("node.group_ungroup")
layout.separator()
# maps node tree type to group node type
@@ -72,7 +73,7 @@ def node_group_items(context):
if not ntree:
return
- yield NodeItemCustom(draw=group_make_draw)
+ yield NodeItemCustom(draw=group_tools_draw)
def contains_group(nodetree, group):
if nodetree == group:
@@ -234,6 +235,8 @@ shader_node_categories = [
NodeItem("ShaderNodeVectorMath"),
NodeItem("ShaderNodeSeparateRGB"),
NodeItem("ShaderNodeCombineRGB"),
+ NodeItem("ShaderNodeSeparateXYZ"),
+ NodeItem("ShaderNodeCombineXYZ"),
NodeItem("ShaderNodeSeparateHSV"),
NodeItem("ShaderNodeCombineHSV"),
NodeItem("ShaderNodeWavelength"),
diff --git a/release/text/readme.html b/release/text/readme.html
index f9cee7418f0..452293ad702 100644
--- a/release/text/readme.html
+++ b/release/text/readme.html
@@ -20,18 +20,18 @@
</style>
</head>
<body>
-<p class="p1"><b>Blender 2.70</b></p>
+<p class="p1"><b>Blender 2.71</b></p>
<p class="p2"><br></p>
<p class="p3"><b>About</b></p>
<p class="p4">Welcome to Blender, the free, open source 3D application for modeling, animation, rendering, compositing, video editing and game creation. Blender is available for Linux, Mac OS X and Windows and has a large world-wide community.</p>
<p class="p4">Blender can be used freely for any purpose, including commercial use and distribution. It's free and open-source software, released under the GNU GPL licence. The entire source code is available on our website.</p>
<p class="p4">For more information, visit <a href="http://www.blender.org/"><span class="s1">blender.org</span></a>.</p>
<p class="p2"><br></p>
-<p class="p3"><b>2.70</b></p>
-<p class="p4">The Blender Foundation and online developer community is proud to present Blender 2.70. This release is the first official stable release of the Blender 2.7 series. <a href="http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.70"><span class="s1">More information about this release</span></a>.</p>
+<p class="p3"><b>2.71</b></p>
+<p class="p4">The Blender Foundation and online developer community is proud to present Blender 2.71. This release is the second official stable release of the Blender 2.7 series. <a href="http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.71"><span class="s1">More information about this release</span></a>.</p>
<p class="p2"><br></p>
<p class="p3"><b>Bugs</b></p>
-<p class="p4">Although Blender 2.70 is considered a stable release, you may encounter a bug. If you do, please help us by posting it in the bug tracker or using Help <span class="s2">→</span> Report a Bug from inside Blender. If it wasn’t reported yet, please log in (or register) and fill in detailed information about the error. Please post detailed instructions on how to reproduce it or post a .blend file showcasing the bug.</p>
+<p class="p4">Although Blender 2.71 is considered a stable release, you may encounter a bug. If you do, please help us by posting it in the bug tracker or using Help <span class="s2">→</span> Report a Bug from inside Blender. If it wasn’t reported yet, please log in (or register) and fill in detailed information about the error. Please post detailed instructions on how to reproduce it or post a .blend file showcasing the bug.</p>
<p class="p2"><br></p>
<p class="p3"><b>Package Contents</b></p>
<p class="p4">The downloaded Blender package includes:</p>
@@ -55,7 +55,7 @@
<p class="p3"><b>Links</b></p>
<p class="p4">Users:</p>
<p class="p5"><span class="s3">General information <a href="http://www.blender.org/"><span class="s4">www.blender.org</span></a> <br>
-Full release log <a href="http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.70"><span class="s4">wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.70</span></a><br>
+Full release log <a href="http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.71"><span class="s4">wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.71</span></a><br>
Tutorials <a href="http://www.blender.org/support/tutorials/"><span class="s4">www.blender.org/support/tutorials/</span></a> <br>
Manual <a href="http://wiki.blender.org/index.php/Doc:2.6/Manual"><span class="s4">wiki.blender.org/index.php/Doc:2.6/Manual</span></a><br>
User Forum <a href="http://www.blenderartists.org/"><span class="s4">www.blenderartists.org</span></a><br>
diff --git a/scons b/scons
-Subproject 2d6ebcb23909058b846aa232ecb2fee497924cf
+Subproject 1ec93106c40fab0c339d09c7ed9897c85ddf3da
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 391fdf42d28..c696719e650 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -29,4 +29,3 @@ if(WITH_GAMEENGINE)
add_subdirectory(gameengine)
endif()
-add_subdirectory(tests)
diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h
index 2a27a3d6f4d..206345582b2 100644
--- a/source/blender/blenfont/BLF_api.h
+++ b/source/blender/blenfont/BLF_api.h
@@ -32,22 +32,26 @@
#ifndef __BLF_API_H__
#define __BLF_API_H__
+#include "BLI_compiler_attrs.h"
+
struct rctf;
struct ColorManagedDisplay;
int BLF_init(int points, int dpi);
void BLF_exit(void);
void BLF_default_dpi(int dpi);
+void BLF_default_set(int fontid);
void BLF_cache_clear(void);
-int BLF_load(const char *name);
-int BLF_load_mem(const char *name, const unsigned char *mem, int mem_size);
+int BLF_load(const char *name) ATTR_NONNULL();
+int BLF_load_mem(const char *name, const unsigned char *mem, int mem_size) ATTR_NONNULL();
-int BLF_load_unique(const char *name);
-int BLF_load_mem_unique(const char *name, const unsigned char *mem, int mem_size);
+int BLF_load_unique(const char *name) ATTR_NONNULL();
+int BLF_load_mem_unique(const char *name, const unsigned char *mem, int mem_size) ATTR_NONNULL();
-void BLF_unload(const char *name);
+void BLF_unload(const char *name) ATTR_NONNULL();
+void BLF_unload_id(int fontid);
/* Attach a file with metrics information from memory. */
void BLF_metrics_attach(int fontid, unsigned char *mem, int mem_size);
@@ -71,8 +75,8 @@ void BLF_size(int fontid, int size, int dpi);
void BLF_matrix(int fontid, const double m[16]);
/* Draw the string using the default font, size and dpi. */
-void BLF_draw_default(float x, float y, float z, const char *str, size_t len);
-void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t len);
+void BLF_draw_default(float x, float y, float z, const char *str, size_t len) ATTR_NONNULL();
+void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t len) ATTR_NONNULL();
/* Draw the string using the current font. */
void BLF_draw(int fontid, const char *str, size_t len);
@@ -80,45 +84,45 @@ void BLF_draw_ascii(int fontid, const char *str, size_t len);
int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth);
/* Get the string byte offset that fits within a given width */
-size_t BLF_width_to_strlen(int fontid, const char *str, size_t len, float width, float *r_width);
+size_t BLF_width_to_strlen(int fontid, const char *str, size_t len, float width, float *r_width) ATTR_NONNULL(2);
/* Same as BLF_width_to_strlen but search from the string end */
-size_t BLF_width_to_rstrlen(int fontid, const char *str, size_t len, float width, float *r_width);
+size_t BLF_width_to_rstrlen(int fontid, const char *str, size_t len, float width, float *r_width) ATTR_NONNULL(2);
/* This function return the bounding box of the string
* and are not multiplied by the aspect.
*/
-void BLF_boundbox(int fontid, const char *str, size_t len, struct rctf *box);
+void BLF_boundbox(int fontid, const char *str, size_t len, struct rctf *box) ATTR_NONNULL();
/* The next both function return the width and height
* of the string, using the current font and both value
* are multiplied by the aspect of the font.
*/
-float BLF_width(int fontid, const char *str, size_t len);
-float BLF_height(int fontid, const char *str, size_t len);
+float BLF_width(int fontid, const char *str, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+float BLF_height(int fontid, const char *str, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
/* Return dimensions of the font without any sample text. */
-float BLF_height_max(int fontid);
-float BLF_width_max(int fontid);
-float BLF_descender(int fontid);
-float BLF_ascender(int fontid);
+float BLF_height_max(int fontid) ATTR_WARN_UNUSED_RESULT;
+float BLF_width_max(int fontid) ATTR_WARN_UNUSED_RESULT;
+float BLF_descender(int fontid) ATTR_WARN_UNUSED_RESULT;
+float BLF_ascender(int fontid) ATTR_WARN_UNUSED_RESULT;
/* The following function return the width and height of the string, but
* just in one call, so avoid extra freetype2 stuff.
*/
-void BLF_width_and_height(int fontid, const char *str, size_t len, float *r_width, float *r_height);
+void BLF_width_and_height(int fontid, const char *str, size_t len, float *r_width, float *r_height) ATTR_NONNULL();
/* For fixed width fonts only, returns the width of a
* character.
*/
-float BLF_fixed_width(int fontid);
+float BLF_fixed_width(int fontid) ATTR_WARN_UNUSED_RESULT;
/* and this two function return the width and height
* of the string, using the default font and both value
* are multiplied by the aspect of the font.
*/
-void BLF_width_and_height_default(const char *str, size_t len, float *r_width, float *r_height);
-float BLF_width_default(const char *str, size_t len);
-float BLF_height_default(const char *str, size_t len);
+void BLF_width_and_height_default(const char *str, size_t len, float *r_width, float *r_height) ATTR_NONNULL();
+float BLF_width_default(const char *str, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+float BLF_height_default(const char *str, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
/* Set rotation for default font. */
void BLF_rotation_default(float angle);
@@ -168,19 +172,19 @@ void BLF_buffer_col(int fontid, float r, float g, float b, float a);
/* Draw the string into the buffer, this function draw in both buffer, float and unsigned char _BUT_
* it's not necessary set both buffer, NULL is valid here.
*/
-void BLF_draw_buffer(int fontid, const char *str);
+void BLF_draw_buffer(int fontid, const char *str) ATTR_NONNULL();
/* Add a path to the font dir paths. */
-void BLF_dir_add(const char *path);
+void BLF_dir_add(const char *path) ATTR_NONNULL();
/* Remove a path from the font dir paths. */
-void BLF_dir_rem(const char *path);
+void BLF_dir_rem(const char *path) ATTR_NONNULL();
/* Return an array with all the font dir (this can be used for filesel) */
-char **BLF_dir_get(int *ndir);
+char **BLF_dir_get(int *ndir) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
/* Free the data return by BLF_dir_get. */
-void BLF_dir_free(char **dirs, int count);
+void BLF_dir_free(char **dirs, int count) ATTR_NONNULL();
#ifdef DEBUG
void BLF_state_print(int fontid);
diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c
index e086ca4977e..cdccbe044bb 100644
--- a/source/blender/blenfont/intern/blf.c
+++ b/source/blender/blenfont/intern/blf.c
@@ -152,6 +152,14 @@ static int blf_search_available(void)
return -1;
}
+void BLF_default_set(int fontid)
+{
+ FontBLF *font = blf_get(fontid);
+ if (font || fontid == -1) {
+ global_font_default = fontid;
+ }
+}
+
static int blf_global_font_init(void)
{
if (global_font_default == -1) {
@@ -335,6 +343,15 @@ void BLF_unload(const char *name)
}
}
+void BLF_unload_id(int fontid)
+{
+ FontBLF *font = blf_get(fontid);
+ if (font) {
+ blf_font_free(font);
+ global_font[fontid] = NULL;
+ }
+}
+
void BLF_enable(int fontid, int option)
{
FontBLF *font = blf_get(fontid);
@@ -460,9 +477,6 @@ void BLF_blur(int fontid, int size)
void BLF_draw_default(float x, float y, float z, const char *str, size_t len)
{
- if (!str)
- return;
-
if (!blf_global_font_init())
return;
@@ -474,9 +488,6 @@ void BLF_draw_default(float x, float y, float z, const char *str, size_t len)
/* same as above but call 'BLF_draw_ascii' */
void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t len)
{
- if (!str)
- return;
-
if (!blf_global_font_init())
return;
diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h
index e4ebe0e0ed5..67d5b1ec6ec 100644
--- a/source/blender/blenkernel/BKE_blender.h
+++ b/source/blender/blenkernel/BKE_blender.h
@@ -41,8 +41,8 @@ extern "C" {
/* these lines are grep'd, watch out for our not-so-awesome regex
* and keep comment above the defines.
* Use STRINGIFY() rather than defining with quotes */
-#define BLENDER_VERSION 270
-#define BLENDER_SUBVERSION 5
+#define BLENDER_VERSION 271
+#define BLENDER_SUBVERSION 0
/* 262 was the last editmesh release but it has compatibility code for bmesh data */
#define BLENDER_MINVERSION 270
#define BLENDER_MINSUBVERSION 5
diff --git a/source/blender/blenkernel/BKE_depsgraph.h b/source/blender/blenkernel/BKE_depsgraph.h
index 58903b76a15..bde15d29a1b 100644
--- a/source/blender/blenkernel/BKE_depsgraph.h
+++ b/source/blender/blenkernel/BKE_depsgraph.h
@@ -57,7 +57,7 @@ struct ListBase;
*/
typedef struct EvaluationContext {
bool for_render; /* Set to true if evaluation shall be performed for render purposes,
- keep at false if update shall happen for the viewport. */
+ * keep at false if update shall happen for the viewport. */
} EvaluationContext;
/* DagNode->eval_flags */
diff --git a/source/blender/blenkernel/BKE_displist.h b/source/blender/blenkernel/BKE_displist.h
index face19adc1e..0afc457f2b5 100644
--- a/source/blender/blenkernel/BKE_displist.h
+++ b/source/blender/blenkernel/BKE_displist.h
@@ -72,7 +72,6 @@ typedef struct DispList {
short col, rt; /* rt used by initrenderNurbs */
float *verts, *nors;
int *index;
- unsigned int *col1, *col2;
int charidx;
int totindex; /* indexed array drawing surfaces */
diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h
index 55c71ff49cf..0372931dc49 100644
--- a/source/blender/blenkernel/BKE_library.h
+++ b/source/blender/blenkernel/BKE_library.h
@@ -77,13 +77,16 @@ int set_listbasepointers(struct Main *main, struct ListBase **lb);
void BKE_libblock_free(struct Main *bmain, void *idv);
void BKE_libblock_free_ex(struct Main *bmain, void *idv, bool do_id_user);
void BKE_libblock_free_us(struct Main *bmain, void *idv);
-void BKE_libblock_free_data(struct ID *id);
+void BKE_libblock_free_data(struct Main *bmain, struct ID *id);
/* Main API */
struct Main *BKE_main_new(void);
void BKE_main_free(struct Main *mainvar);
+void BKE_main_lock(struct Main *bmain);
+void BKE_main_unlock(struct Main *bmain);
+
void BKE_main_id_tag_idcode(struct Main *mainvar, const short type, const bool tag);
void BKE_main_id_tag_listbase(struct ListBase *lb, const bool tag);
void BKE_main_id_tag_all(struct Main *mainvar, const bool tag);
diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h
index 721866daff5..82b03127237 100644
--- a/source/blender/blenkernel/BKE_main.h
+++ b/source/blender/blenkernel/BKE_main.h
@@ -48,6 +48,7 @@ extern "C" {
struct EvaluationContext;
struct Library;
+struct MainLock;
typedef struct Main {
struct Main *next, *prev;
@@ -96,6 +97,8 @@ typedef struct Main {
/* Evaluation context used by viewport */
struct EvaluationContext *eval_ctx;
+
+ struct MainLock *lock;
} Main;
#define MAIN_VERSION_ATLEAST(main, ver, subver) \
diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h
index f3f6b556a07..ed7e506941c 100644
--- a/source/blender/blenkernel/BKE_mesh_mapping.h
+++ b/source/blender/blenkernel/BKE_mesh_mapping.h
@@ -131,7 +131,8 @@ int *BKE_mesh_calc_smoothgroups(
/* No good (portable) way to have exported inlined functions... */
#define BKE_MESH_TESSFACE_VINDEX_ORDER(_mf, _v) ( \
- (CHECK_TYPE_INLINE(_mf, MFace *), CHECK_TYPE_INLINE(_v, unsigned int)), \
+ (CHECK_TYPE_INLINE(_mf, MFace *), \
+ CHECK_TYPE_INLINE(&(_v), unsigned int *)), \
((_mf->v1 == _v) ? 0 : \
(_mf->v2 == _v) ? 1 : \
(_mf->v3 == _v) ? 2 : \
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index 3b2bafa799f..75616b9df78 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -111,7 +111,10 @@ typedef enum ModifierApplyFlag {
MOD_APPLY_RENDER = 1 << 0, /* Render time. */
MOD_APPLY_USECACHE = 1 << 1, /* Result of evaluation will be cached, so modifier might
* want to cache data for quick updates (used by subsurf) */
- MOD_APPLY_ORCO = 1 << 2 /* Modifier evaluated for undeformed texture coordinates */
+ MOD_APPLY_ORCO = 1 << 2, /* Modifier evaluated for undeformed texture coordinates */
+ MOD_APPLY_IGNORE_SIMPLIFY = 1 << 3, /* Ignore scene simplification flag and use subdivisions
+ * level set in multires modifier.
+ */
} ModifierApplyFlag;
diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h
index aa09fe1ce8d..11d81a149b1 100644
--- a/source/blender/blenkernel/BKE_multires.h
+++ b/source/blender/blenkernel/BKE_multires.h
@@ -65,7 +65,8 @@ void multiresModifier_set_levels_from_disps(struct MultiresModifierData *mmd, st
typedef enum {
MULTIRES_USE_LOCAL_MMD = 1,
MULTIRES_USE_RENDER_PARAMS = 2,
- MULTIRES_ALLOC_PAINT_MASK = 4
+ MULTIRES_ALLOC_PAINT_MASK = 4,
+ MULTIRES_IGNORE_SIMPLIFY = 8
} MultiresFlags;
struct DerivedMesh *multires_make_derived_from_derived(struct DerivedMesh *dm,
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 13a32ee1528..c81133a85fa 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -727,7 +727,7 @@ struct ShadeResult;
#define SH_NODE_BRIGHTCONTRAST 165
#define SH_NODE_LIGHT_FALLOFF 166
#define SH_NODE_OBJECT_INFO 167
-#define SH_NODE_PARTICLE_INFO 168
+#define SH_NODE_PARTICLE_INFO 168
#define SH_NODE_TEX_BRICK 169
#define SH_NODE_BUMP 170
#define SH_NODE_SCRIPT 171
@@ -746,7 +746,9 @@ struct ShadeResult;
#define SH_NODE_COMBHSV 184
#define SH_NODE_BSDF_HAIR 185
#define SH_NODE_LAMP 186
-#define SH_NODE_UVMAP 187
+#define SH_NODE_UVMAP 187
+#define SH_NODE_SEPXYZ 188
+#define SH_NODE_COMBXYZ 189
/* custom defines options for Material node */
#define SH_NODE_MAT_DIFF 1
diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h
index d7622260416..53e41c1dec8 100644
--- a/source/blender/blenkernel/BKE_pbvh.h
+++ b/source/blender/blenkernel/BKE_pbvh.h
@@ -294,7 +294,7 @@ void pbvh_vertex_iter_init(PBVH *bvh, PBVHNode *node,
vi.mask = vi.key->has_mask ? CCG_elem_mask(vi.key, vi.grid) : NULL; \
vi.grid = CCG_elem_next(vi.key, vi.grid); \
if (vi.gh) { \
- if (BLI_BITMAP_GET(vi.gh, vi.gy * vi.gridsize + vi.gx)) \
+ if (BLI_BITMAP_TEST(vi.gh, vi.gy * vi.gridsize + vi.gx)) \
continue; \
} \
} \
diff --git a/source/blender/blenkernel/BKE_unit.h b/source/blender/blenkernel/BKE_unit.h
index 07aeceda474..598817298b8 100644
--- a/source/blender/blenkernel/BKE_unit.h
+++ b/source/blender/blenkernel/BKE_unit.h
@@ -61,17 +61,19 @@ const char *bUnit_GetNameDisplay(void *usys_pt, int index);
double bUnit_GetScaler(void *usys_pt, int index);
/* aligned with PropertyUnit */
-#define B_UNIT_NONE 0
-#define B_UNIT_LENGTH 1
-#define B_UNIT_AREA 2
-#define B_UNIT_VOLUME 3
-#define B_UNIT_MASS 4
-#define B_UNIT_ROTATION 5
-#define B_UNIT_TIME 6
-#define B_UNIT_VELOCITY 7
-#define B_UNIT_ACCELERATION 8
-#define B_UNIT_CAMERA 9
-#define B_UNIT_TYPE_TOT 10
+enum {
+ B_UNIT_NONE = 0,
+ B_UNIT_LENGTH = 1,
+ B_UNIT_AREA = 2,
+ B_UNIT_VOLUME = 3,
+ B_UNIT_MASS = 4,
+ B_UNIT_ROTATION = 5,
+ B_UNIT_TIME = 6,
+ B_UNIT_VELOCITY = 7,
+ B_UNIT_ACCELERATION = 8,
+ B_UNIT_CAMERA = 9,
+ B_UNIT_TYPE_TOT = 10,
+};
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c
index ff1d2191e51..a70238da34e 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.c
+++ b/source/blender/blenkernel/intern/DerivedMesh.c
@@ -614,7 +614,8 @@ void DM_to_mesh(DerivedMesh *dm, Mesh *me, Object *ob, CustomDataMask mask)
MEM_freeN(me->mselect);
}
- *me = tmp;
+ /* skip the listbase */
+ MEMCPY_STRUCT_OFS(me, &tmp, id.prev);
}
void DM_to_meshkey(DerivedMesh *dm, Mesh *me, KeyBlock *kb)
@@ -1212,14 +1213,15 @@ static void calc_weightpaint_vert_array(Object *ob, DerivedMesh *dm, int const d
}
}
else {
- int col_i;
+ unsigned char col[4];
if (draw_flag & (CALC_WP_GROUP_USER_ACTIVE | CALC_WP_GROUP_USER_ALL)) {
- col_i = 0;
+ copy_v3_v3_char((char *)col, dm_wcinfo->alert_color);
+ col[3] = 255;
}
else {
- weightpaint_color((unsigned char *)&col_i, dm_wcinfo, 0.0f);
+ weightpaint_color(col, dm_wcinfo, 0.0f);
}
- fill_vn_i((int *)r_wtcol_v, numVerts, col_i);
+ fill_vn_i((int *)r_wtcol_v, numVerts, *((int *)col));
}
}
diff --git a/source/blender/blenkernel/intern/anim.c b/source/blender/blenkernel/intern/anim.c
index b8e76858ffe..aff99d3e1bf 100644
--- a/source/blender/blenkernel/intern/anim.c
+++ b/source/blender/blenkernel/intern/anim.c
@@ -513,7 +513,7 @@ void calc_curvepath(Object *ob, ListBase *nurbs)
dist = (float *)MEM_mallocN(sizeof(float) * (tot + 1), "calcpathdist");
/* all lengths in *dist */
- bevp = bevpfirst = (BevPoint *)(bl + 1);
+ bevp = bevpfirst = bl->bevpoints;
fp = dist;
*fp = 0.0f;
for (a = 0; a < tot; a++) {
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index f956474701f..7e4552469d6 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -986,8 +986,8 @@ void BKE_all_animdata_fix_paths_rename(ID *ref_id, const char *prefix, const cha
AnimData *adt = BKE_animdata_from_id(id); \
NtId_Type *ntp = (NtId_Type *)id; \
if (ntp->nodetree) { \
- AnimData *adt2 = BKE_animdata_from_id((ID *)ntp); \
- BKE_animdata_fix_paths_rename((ID *)ntp, adt2, ref_id, prefix, oldName, newName, 0, 0, 1); \
+ AnimData *adt2 = BKE_animdata_from_id((ID *)ntp->nodetree); \
+ BKE_animdata_fix_paths_rename((ID *)ntp->nodetree, adt2, ref_id, prefix, oldName, newName, 0, 0, 1); \
} \
BKE_animdata_fix_paths_rename(id, adt, ref_id, prefix, oldName, newName, 0, 0, 1); \
} (void)0
@@ -1168,7 +1168,7 @@ KS_Path *BKE_keyingset_add_path(KeyingSet *ks, ID *id, const char group_name[],
/* just copy path info */
/* TODO: should array index be checked too? */
- ksp->rna_path = BLI_strdupn(rna_path, strlen(rna_path));
+ ksp->rna_path = BLI_strdup(rna_path);
ksp->array_index = array_index;
/* store flags */
diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c
index 8b87f5b0cea..a71d3987c58 100644
--- a/source/blender/blenkernel/intern/blender.c
+++ b/source/blender/blenkernel/intern/blender.c
@@ -384,6 +384,7 @@ void BKE_userdef_free(void)
wmKeyMapItem *kmi;
wmKeyMapDiffItem *kmdi;
bAddon *addon, *addon_next;
+ uiFont *font;
for (km = U.user_keymaps.first; km; km = km->next) {
for (kmdi = km->diff_items.first; kmdi; kmdi = kmdi->next) {
@@ -413,6 +414,12 @@ void BKE_userdef_free(void)
MEM_freeN(addon);
}
+ for (font = U.uifonts.first; font; font = font->next) {
+ BLF_unload_id(font->blf_id);
+ }
+
+ BLF_default_set(-1);
+
BLI_freelistN(&U.autoexec_paths);
BLI_freelistN(&U.uistyles);
@@ -661,7 +668,7 @@ void BKE_write_undo(bContext *C, const char *name)
counter = counter % U.undosteps;
BLI_snprintf(numstr, sizeof(numstr), "%d.blend", counter);
- BLI_make_file_string("/", filepath, BLI_temporary_dir(), numstr);
+ BLI_make_file_string("/", filepath, BLI_temp_dir_session(), numstr);
/* success = */ /* UNUSED */ BLO_write_file(CTX_data_main(C), filepath, fileflags, NULL, NULL);
diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c
index 3fca58bd088..2e1530740d8 100644
--- a/source/blender/blenkernel/intern/bpath.c
+++ b/source/blender/blenkernel/intern/bpath.c
@@ -695,7 +695,7 @@ bool BKE_bpath_relocate_visitor(void *pathbase_v, char *path_dst, const char *pa
/* -------------------------------------------------------------------- */
/**
* Backup/Restore/Free functions,
- * \note These functions assume the data won't chane order.
+ * \note These functions assume the data won't change order.
*/
struct PathStore {
diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c
index f2874c276ff..3cfa22a2dc6 100644
--- a/source/blender/blenkernel/intern/cdderivedmesh.c
+++ b/source/blender/blenkernel/intern/cdderivedmesh.c
@@ -38,6 +38,7 @@
#include "BLI_blenlib.h"
#include "BLI_edgehash.h"
#include "BLI_utildefines.h"
+#include "BLI_stackdefines.h"
#include "BKE_pbvh.h"
#include "BKE_cdderivedmesh.h"
@@ -2769,17 +2770,7 @@ DerivedMesh *CDDM_merge_verts(DerivedMesh *dm, const int *vtargetmap, const int
MEM_freeN(oldv);
MEM_freeN(olde);
MEM_freeN(oldl);
- MEM_freeN(oldp);
-
- STACK_FREE(oldv);
- STACK_FREE(olde);
- STACK_FREE(oldl);
- STACK_FREE(oldp);
-
- STACK_FREE(mvert);
- STACK_FREE(medge);
- STACK_FREE(mloop);
- STACK_FREE(mpoly);
+ MEM_freeN(oldp);;
BLI_edgehash_free(ehash, NULL);
@@ -2804,15 +2795,15 @@ void CDDM_calc_edges_tessface(DerivedMesh *dm)
eh = BLI_edgeset_new_ex(__func__, BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(numFaces));
for (i = 0; i < numFaces; i++, mf++) {
- BLI_edgeset_reinsert(eh, mf->v1, mf->v2);
- BLI_edgeset_reinsert(eh, mf->v2, mf->v3);
+ BLI_edgeset_add(eh, mf->v1, mf->v2);
+ BLI_edgeset_add(eh, mf->v2, mf->v3);
if (mf->v4) {
- BLI_edgeset_reinsert(eh, mf->v3, mf->v4);
- BLI_edgeset_reinsert(eh, mf->v4, mf->v1);
+ BLI_edgeset_add(eh, mf->v3, mf->v4);
+ BLI_edgeset_add(eh, mf->v4, mf->v1);
}
else {
- BLI_edgeset_reinsert(eh, mf->v3, mf->v1);
+ BLI_edgeset_add(eh, mf->v3, mf->v1);
}
}
diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c
index f5c7f9e4501..885f186faf3 100644
--- a/source/blender/blenkernel/intern/cloth.c
+++ b/source/blender/blenkernel/intern/cloth.c
@@ -1323,14 +1323,14 @@ static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm )
/* insert other near springs in edgeset AFTER bending springs are calculated (for selfcolls) */
for (i = 0; i < numedges; i++) { /* struct springs */
- BLI_edgeset_reinsert(edgeset, medge[i].v1, medge[i].v2);
+ BLI_edgeset_add(edgeset, medge[i].v1, medge[i].v2);
}
for (i = 0; i < numfaces; i++) { /* edge springs */
if (mface[i].v4) {
- BLI_edgeset_reinsert(edgeset, mface[i].v1, mface[i].v3);
+ BLI_edgeset_add(edgeset, mface[i].v1, mface[i].v3);
- BLI_edgeset_reinsert(edgeset, mface[i].v2, mface[i].v4);
+ BLI_edgeset_add(edgeset, mface[i].v2, mface[i].v4);
}
}
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index a2fb06e9797..b27655c8c19 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -1158,7 +1158,6 @@ static void followpath_get_tarmat(bConstraint *con, bConstraintOb *cob, bConstra
if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVE)) {
Curve *cu = ct->tar->data;
float vec[4], dir[3], radius;
- float totmat[4][4] = MAT4_UNITY;
float curvetime;
unit_m4(ct->matrix);
@@ -1206,6 +1205,9 @@ static void followpath_get_tarmat(bConstraint *con, bConstraintOb *cob, bConstra
}
if (where_on_path(ct->tar, curvetime, vec, dir, (data->followflag & FOLLOWPATH_FOLLOW) ? quat : NULL, &radius, NULL) ) { /* quat_pt is quat or NULL*/
+ float totmat[4][4];
+ unit_m4(totmat);
+
if (data->followflag & FOLLOWPATH_FOLLOW) {
#if 0
float x1, q[4];
@@ -3039,11 +3041,12 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVE)) {
float obmat[4][4], ownLoc[3];
float curveMin[3], curveMax[3];
- float targetMatrix[4][4] = MAT4_UNITY;
+ float targetMatrix[4][4];
copy_m4_m4(obmat, cob->matrix);
copy_v3_v3(ownLoc, obmat[3]);
+ unit_m4(targetMatrix);
INIT_MINMAX(curveMin, curveMax);
/* XXX - don't think this is good calling this here - campbell */
BKE_object_minmax(ct->tar, curveMin, curveMax, true);
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index 89ba2e9d68b..47b29b49689 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -290,7 +290,7 @@ static int ctx_data_get(bContext *C, const char *member, bContextDataResult *res
return done;
/* we check recursion to ensure that we do not get infinite
- * loops requesting data from ourselfs in a context callback */
+ * loops requesting data from ourselves in a context callback */
/* Ok, this looks evil...
* if (ret) done = -(-ret | -done);
diff --git a/source/blender/blenkernel/intern/crazyspace.c b/source/blender/blenkernel/intern/crazyspace.c
index 3fde1cdd710..7ca5d6b4f28 100644
--- a/source/blender/blenkernel/intern/crazyspace.c
+++ b/source/blender/blenkernel/intern/crazyspace.c
@@ -93,12 +93,12 @@ static void make_vertexcos__mapFunc(void *userData, int index, const float co[3]
{
MappedUserData *mappedData = (MappedUserData *)userData;
- if (BLI_BITMAP_GET(mappedData->vertex_visit, index) == 0) {
+ if (BLI_BITMAP_TEST(mappedData->vertex_visit, index) == 0) {
/* we need coord from prototype vertex, not from copies,
* assume they stored in the beginning of vertex array stored in DM
* (mirror modifier for eg does this) */
copy_v3_v3(mappedData->vertexcos[index], co);
- BLI_BITMAP_SET(mappedData->vertex_visit, index);
+ BLI_BITMAP_ENABLE(mappedData->vertex_visit, index);
}
}
diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c
index 4de034d3904..0abe956b599 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -1085,12 +1085,6 @@ void BKE_nurb_makeFaces(Nurb *nu, float *coord_array, int rowstride, int resolu,
sum = (float *)MEM_callocN(sizeof(float) * len, "makeNurbfaces1");
- len = totu * totv;
- if (len == 0) {
- MEM_freeN(sum);
- return;
- }
-
bp = nu->bp;
i = nu->pntsu * nu->pntsv;
ratcomp = 0;
@@ -1193,6 +1187,8 @@ void BKE_nurb_makeFaces(Nurb *nu, float *coord_array, int rowstride, int resolu,
}
}
+ zero_v3(in);
+
/* one! (1.0) real point now */
fp = sum;
for (j = jsta; j <= jen; j++) {
@@ -1307,6 +1303,8 @@ void BKE_nurb_makeCurve(Nurb *nu, float *coord_array, float *tilt_array, float *
}
}
+ zero_v3(coord_fp);
+
/* one! (1.0) real point */
fp = sum;
bp = nu->bp + istart - 1;
@@ -1473,7 +1471,7 @@ float *BKE_curve_surf_make_orco(Object *ob)
}
else {
int size = (nu->pntsu * resolu) * (nu->pntsv * resolv) * 3 * sizeof(float);
- float *_tdata = MEM_callocN(size, "temp data");
+ float *_tdata = MEM_mallocN(size, "temp data");
float *tdata = _tdata;
BKE_nurb_makeFaces(nu, tdata, 0, resolu, resolv);
@@ -1676,7 +1674,7 @@ void BKE_curve_bevel_make(Scene *scene, Object *ob, ListBase *disp,
}
else if (cu->ext2 == 0.0f) {
dl = MEM_callocN(sizeof(DispList), "makebevelcurve2");
- dl->verts = MEM_mallocN(2 * 3 * sizeof(float), "makebevelcurve2");
+ dl->verts = MEM_mallocN(2 * sizeof(float[3]), "makebevelcurve2");
BLI_addtail(disp, dl);
dl->type = DL_SEGM;
dl->parts = 1;
@@ -1693,7 +1691,7 @@ void BKE_curve_bevel_make(Scene *scene, Object *ob, ListBase *disp,
nr = 4 + 2 * cu->bevresol;
dl = MEM_callocN(sizeof(DispList), "makebevelcurve p1");
- dl->verts = MEM_mallocN(nr * 3 * sizeof(float), "makebevelcurve p1");
+ dl->verts = MEM_mallocN(nr * sizeof(float[3]), "makebevelcurve p1");
BLI_addtail(disp, dl);
dl->type = DL_POLY;
dl->parts = 1;
@@ -1725,7 +1723,7 @@ void BKE_curve_bevel_make(Scene *scene, Object *ob, ListBase *disp,
nr = 3 + 2 * cu->bevresol;
dl = MEM_callocN(sizeof(DispList), "makebevelcurve p1");
- dl->verts = MEM_mallocN(nr * 3 * sizeof(float), "makebevelcurve p1");
+ dl->verts = MEM_mallocN(nr * sizeof(float[3]), "makebevelcurve p1");
BLI_addtail(disp, dl);
dl->type = DL_SEGM;
dl->parts = 1;
@@ -1751,7 +1749,7 @@ void BKE_curve_bevel_make(Scene *scene, Object *ob, ListBase *disp,
nr = 2;
dl = MEM_callocN(sizeof(DispList), "makebevelcurve p2");
- dl->verts = MEM_callocN(nr * 3 * sizeof(float), "makebevelcurve p2");
+ dl->verts = MEM_mallocN(nr * sizeof(float[3]), "makebevelcurve p2");
BLI_addtail(disp, dl);
dl->type = DL_SEGM;
dl->parts = 1;
@@ -1783,7 +1781,7 @@ void BKE_curve_bevel_make(Scene *scene, Object *ob, ListBase *disp,
nr = 3 + 2 * cu->bevresol;
dl = MEM_callocN(sizeof(DispList), "makebevelcurve p3");
- dl->verts = MEM_mallocN(nr * 3 * sizeof(float), "makebevelcurve p3");
+ dl->verts = MEM_mallocN(nr * sizeof(float[3]), "makebevelcurve p3");
BLI_addtail(disp, dl);
dl->type = DL_SEGM;
dl->flag = DL_FRONT_CURVE;
@@ -1855,7 +1853,7 @@ static bool bevelinside(BevList *bl1, BevList *bl2)
/* take first vertex of possible hole */
- bevp = (BevPoint *)(bl2 + 1);
+ bevp = bl2->bevpoints;
hvec1[0] = bevp->vec[0];
hvec1[1] = bevp->vec[1];
hvec1[2] = 0.0;
@@ -1865,7 +1863,7 @@ static bool bevelinside(BevList *bl1, BevList *bl2)
/* test it with all edges of potential surounding poly */
/* count number of transitions left-right */
- bevp = (BevPoint *)(bl1 + 1);
+ bevp = bl1->bevpoints;
nr = bl1->nr;
prevbevp = bevp + (nr - 1);
@@ -2051,7 +2049,7 @@ static void bevel_list_calc_bisect(BevList *bl)
bool is_cyclic = bl->poly != -1;
if (is_cyclic) {
- bevp2 = (BevPoint *)(bl + 1);
+ bevp2 = bl->bevpoints;
bevp1 = bevp2 + (bl->nr - 1);
bevp0 = bevp1 - 1;
nr = bl->nr;
@@ -2065,7 +2063,7 @@ static void bevel_list_calc_bisect(BevList *bl)
* of direction for this guys.
*/
- bevp0 = (BevPoint *)(bl + 1);
+ bevp0 = bl->bevpoints;
bevp1 = bevp0 + 1;
bevp2 = bevp1 + 1;
@@ -2086,7 +2084,7 @@ static void bevel_list_flip_tangents(BevList *bl)
BevPoint *bevp2, *bevp1, *bevp0;
int nr;
- bevp2 = (BevPoint *)(bl + 1);
+ bevp2 = bl->bevpoints;
bevp1 = bevp2 + (bl->nr - 1);
bevp0 = bevp1 - 1;
@@ -2107,7 +2105,7 @@ static void bevel_list_apply_tilt(BevList *bl)
int nr;
float q[4];
- bevp2 = (BevPoint *)(bl + 1);
+ bevp2 = bl->bevpoints;
bevp1 = bevp2 + (bl->nr - 1);
nr = bl->nr;
@@ -2131,7 +2129,7 @@ static void bevel_list_smooth(BevList *bl, int smooth_iter)
int a;
for (a = 0; a < smooth_iter; a++) {
- bevp2 = (BevPoint *)(bl + 1);
+ bevp2 = bl->bevpoints;
bevp1 = bevp2 + (bl->nr - 1);
bevp0 = bevp1 - 1;
@@ -2177,7 +2175,7 @@ static void bevel_list_smooth(BevList *bl, int smooth_iter)
static void make_bevel_list_3D_zup(BevList *bl)
{
- BevPoint *bevp = (BevPoint *)(bl + 1);
+ BevPoint *bevp = bl->bevpoints;
int nr = bl->nr;
bevel_list_calc_bisect(bl);
@@ -2212,7 +2210,7 @@ static void make_bevel_list_3D_minimum_twist(BevList *bl)
bevel_list_calc_bisect(bl);
- bevp2 = (BevPoint *)(bl + 1);
+ bevp2 = bl->bevpoints;
bevp1 = bevp2 + (bl->nr - 1);
bevp0 = bevp1 - 1;
@@ -2250,7 +2248,7 @@ static void make_bevel_list_3D_minimum_twist(BevList *bl)
BevPoint *bevp_last;
- bevp_first = (BevPoint *)(bl + 1);
+ bevp_first = bl->bevpoints;
bevp_first += bl->nr - 1;
bevp_last = bevp_first;
bevp_last--;
@@ -2278,7 +2276,7 @@ static void make_bevel_list_3D_minimum_twist(BevList *bl)
if (angle_normalized_v3v3(bevp_first->dir, cross_tmp) < DEG2RADF(90.0f))
angle = -angle;
- bevp2 = (BevPoint *)(bl + 1);
+ bevp2 = bl->bevpoints;
bevp1 = bevp2 + (bl->nr - 1);
bevp0 = bevp1 - 1;
@@ -2300,11 +2298,11 @@ static void make_bevel_list_3D_minimum_twist(BevList *bl)
* using it's own direction, which might not correspond
* the twist of neighbor point.
*/
- bevp1 = (BevPoint *)(bl + 1);
+ bevp1 = bl->bevpoints;
bevp0 = bevp1 + 1;
minimum_twist_between_two_points(bevp1, bevp0);
- bevp2 = (BevPoint *)(bl + 1);
+ bevp2 = bl->bevpoints;
bevp1 = bevp2 + (bl->nr - 1);
bevp0 = bevp1 - 1;
minimum_twist_between_two_points(bevp1, bevp0);
@@ -2322,7 +2320,7 @@ static void make_bevel_list_3D_tangent(BevList *bl)
bevel_list_flip_tangents(bl);
/* correct the tangents */
- bevp2 = (BevPoint *)(bl + 1);
+ bevp2 = bl->bevpoints;
bevp1 = bevp2 + (bl->nr - 1);
bevp0 = bevp1 - 1;
@@ -2340,7 +2338,7 @@ static void make_bevel_list_3D_tangent(BevList *bl)
/* now for the real twist calc */
- bevp2 = (BevPoint *)(bl + 1);
+ bevp2 = bl->bevpoints;
bevp1 = bevp2 + (bl->nr - 1);
bevp0 = bevp1 - 1;
@@ -2387,7 +2385,7 @@ static void make_bevel_list_segment_3D(BevList *bl)
{
float q[4];
- BevPoint *bevp2 = (BevPoint *)(bl + 1);
+ BevPoint *bevp2 = bl->bevpoints;
BevPoint *bevp1 = bevp2 + 1;
/* simple quat/dir */
@@ -2406,7 +2404,7 @@ static void make_bevel_list_segment_3D(BevList *bl)
/* only for 2 points */
static void make_bevel_list_segment_2D(BevList *bl)
{
- BevPoint *bevp2 = (BevPoint *)(bl + 1);
+ BevPoint *bevp2 = bl->bevpoints;
BevPoint *bevp1 = bevp2 + 1;
const float x1 = bevp1->vec[0] - bevp2->vec[0];
@@ -2429,13 +2427,13 @@ static void make_bevel_list_2D(BevList *bl)
int nr;
if (bl->poly != -1) {
- bevp2 = (BevPoint *)(bl + 1);
+ bevp2 = bl->bevpoints;
bevp1 = bevp2 + (bl->nr - 1);
bevp0 = bevp1 - 1;
nr = bl->nr;
}
else {
- bevp0 = (BevPoint *)(bl + 1);
+ bevp0 = bl->bevpoints;
bevp1 = bevp0 + 1;
bevp2 = bevp1 + 1;
@@ -2467,14 +2465,14 @@ static void make_bevel_list_2D(BevList *bl)
float angle;
/* first */
- bevp = (BevPoint *)(bl + 1);
+ bevp = bl->bevpoints;
angle = atan2(bevp->dir[0], bevp->dir[1]) - M_PI / 2.0;
bevp->sina = sinf(angle);
bevp->cosa = cosf(angle);
vec_to_quat(bevp->quat, bevp->dir, 5, 1);
/* last */
- bevp = (BevPoint *)(bl + 1);
+ bevp = bl->bevpoints;
bevp += (bl->nr - 1);
angle = atan2(bevp->dir[0], bevp->dir[1]) - M_PI / 2.0;
bevp->sina = sinf(angle);
@@ -2489,7 +2487,7 @@ static void bevlist_firstlast_direction_calc_from_bpoint(Nurb *nu, BevList *bl)
BPoint *first_bp = nu->bp, *last_bp = nu->bp + (nu->pntsu - 1);
BevPoint *first_bevp, *last_bevp;
- first_bevp = (BevPoint *)(bl + 1);
+ first_bevp = bl->bevpoints;
last_bevp = first_bevp + (bl->nr - 1);
sub_v3_v3v3(first_bevp->dir, (first_bp + 1)->vec, first_bp->vec);
@@ -2505,7 +2503,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
/*
* - convert all curves to polys, with indication of resol and flags for double-vertices
* - possibly; do a smart vertice removal (in case Nurb)
- * - separate in individual blicks with BoundBox
+ * - separate in individual blocks with BoundBox
* - AutoHole detection
*/
Curve *cu;
@@ -2570,7 +2568,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
bl->nr = len;
bl->dupe_nr = 0;
bl->charidx = nu->charidx;
- bevp = (BevPoint *)(bl + 1);
+ bevp = bl->bevpoints;
bp = nu->bp;
while (len--) {
@@ -2595,7 +2593,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
bl->poly = (nu->flagu & CU_NURB_CYCLIC) ? 0 : -1;
bl->charidx = nu->charidx;
- bevp = (BevPoint *)(bl + 1);
+ bevp = bl->bevpoints;
a = nu->pntsu - 1;
bezt = nu->bezt;
@@ -2689,7 +2687,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
bl->dupe_nr = 0;
bl->poly = (nu->flagu & CU_NURB_CYCLIC) ? 0 : -1;
bl->charidx = nu->charidx;
- bevp = (BevPoint *)(bl + 1);
+ bevp = bl->bevpoints;
BKE_nurb_makeCurve(nu, &bevp->vec[0],
do_tilt ? &bevp->alfa : NULL,
@@ -2712,11 +2710,11 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
bool is_cyclic = bl->poly != -1;
nr = bl->nr;
if (is_cyclic) {
- bevp1 = (BevPoint *)(bl + 1);
+ bevp1 = bl->bevpoints;
bevp0 = bevp1 + (nr - 1);
}
else {
- bevp0 = (BevPoint *)(bl + 1);
+ bevp0 = bl->bevpoints;
bevp1 = bevp0 + 1;
}
nr--;
@@ -2745,8 +2743,8 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
blnew->nr = 0;
BLI_remlink(bev, bl);
BLI_insertlinkbefore(bev, blnext, blnew); /* to make sure bevlijst is tuned with nurblist */
- bevp0 = (BevPoint *)(bl + 1);
- bevp1 = (BevPoint *)(blnew + 1);
+ bevp0 = bl->bevpoints;
+ bevp1 = blnew->bevpoints;
nr = bl->nr;
while (nr--) {
if (bevp0->dupe_tag == 0) {
@@ -2782,7 +2780,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
if (bl->poly > 0) {
min = 300000.0;
- bevp = (BevPoint *)(bl + 1);
+ bevp = bl->bevpoints;
nr = bl->nr;
while (nr--) {
if (min > bevp->vec[0]) {
@@ -2794,14 +2792,14 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
sd->bl = bl;
sd->left = min;
- bevp = (BevPoint *)(bl + 1);
+ bevp = bl->bevpoints;
if (bevp1 == bevp)
bevp0 = bevp + (bl->nr - 1);
else
bevp0 = bevp1 - 1;
bevp = bevp + (bl->nr - 1);
if (bevp1 == bevp)
- bevp2 = (BevPoint *)(bl + 1);
+ bevp2 = bl->bevpoints;
else
bevp2 = bevp1 + 1;
@@ -2840,7 +2838,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
for (a = 0; a < poly; a++, sd++) {
if (sd->bl->hole == sd->dir) {
bl = sd->bl;
- bevp1 = (BevPoint *)(bl + 1);
+ bevp1 = bl->bevpoints;
bevp2 = bevp1 + (bl->nr - 1);
nr = bl->nr / 2;
while (nr--) {
@@ -2859,7 +2857,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
/* 2D Curves */
for (bl = bev->first; bl; bl = bl->next) {
if (bl->nr < 2) {
- BevPoint *bevp = (BevPoint *)(bl + 1);
+ BevPoint *bevp = bl->bevpoints;
unit_qt(bevp->quat);
}
else if (bl->nr == 2) { /* 2 pnt, treat separate */
@@ -2874,7 +2872,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
/* 3D Curves */
for (bl = bev->first; bl; bl = bl->next) {
if (bl->nr < 2) {
- BevPoint *bevp = (BevPoint *)(bl + 1);
+ BevPoint *bevp = bl->bevpoints;
unit_qt(bevp->quat);
}
else if (bl->nr == 2) { /* 2 pnt, treat separate */
diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.c
index 613bb7cdabc..cf894e72324 100644
--- a/source/blender/blenkernel/intern/displist.c
+++ b/source/blender/blenkernel/intern/displist.c
@@ -71,8 +71,6 @@ void BKE_displist_elem_free(DispList *dl)
if (dl->verts) MEM_freeN(dl->verts);
if (dl->nors) MEM_freeN(dl->nors);
if (dl->index) MEM_freeN(dl->index);
- if (dl->col1) MEM_freeN(dl->col1);
- if (dl->col2) MEM_freeN(dl->col2);
if (dl->bevelSplitFlag) MEM_freeN(dl->bevelSplitFlag);
MEM_freeN(dl);
}
@@ -145,8 +143,6 @@ void BKE_displist_copy(ListBase *lbn, ListBase *lb)
dln->verts = MEM_dupallocN(dl->verts);
dln->nors = MEM_dupallocN(dl->nors);
dln->index = MEM_dupallocN(dl->index);
- dln->col1 = MEM_dupallocN(dl->col1);
- dln->col2 = MEM_dupallocN(dl->col2);
if (dl->bevelSplitFlag)
dln->bevelSplitFlag = MEM_dupallocN(dl->bevelSplitFlag);
@@ -347,7 +343,7 @@ static void curve_to_displist(Curve *cu, ListBase *nubase, ListBase *dispbase,
dl = MEM_callocN(sizeof(DispList), "makeDispListbez");
/* len+1 because of 'forward_diff_bezier' function */
- dl->verts = MEM_callocN((len + 1) * 3 * sizeof(float), "dlverts");
+ dl->verts = MEM_mallocN((len + 1) * sizeof(float[3]), "dlverts");
BLI_addtail(dispbase, dl);
dl->parts = 1;
dl->nr = len;
@@ -401,7 +397,7 @@ static void curve_to_displist(Curve *cu, ListBase *nubase, ListBase *dispbase,
len = (resolu * SEGMENTSU(nu));
dl = MEM_callocN(sizeof(DispList), "makeDispListsurf");
- dl->verts = MEM_callocN(len * 3 * sizeof(float), "dlverts");
+ dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(dispbase, dl);
dl->parts = 1;
@@ -418,7 +414,7 @@ static void curve_to_displist(Curve *cu, ListBase *nubase, ListBase *dispbase,
else if (nu->type == CU_POLY) {
len = nu->pntsu;
dl = MEM_callocN(sizeof(DispList), "makeDispListpoly");
- dl->verts = MEM_callocN(len * 3 * sizeof(float), "dlverts");
+ dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(dispbase, dl);
dl->parts = 1;
dl->nr = len;
@@ -1227,7 +1223,7 @@ void BKE_displist_make_surf(Scene *scene, Object *ob, ListBase *dispbase,
len = SEGMENTSU(nu) * resolu;
dl = MEM_callocN(sizeof(DispList), "makeDispListsurf");
- dl->verts = MEM_callocN(len * 3 * sizeof(float), "dlverts");
+ dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(dispbase, dl);
dl->parts = 1;
@@ -1249,7 +1245,7 @@ void BKE_displist_make_surf(Scene *scene, Object *ob, ListBase *dispbase,
len = (nu->pntsu * resolu) * (nu->pntsv * resolv);
dl = MEM_callocN(sizeof(DispList), "makeDispListsurf");
- dl->verts = MEM_callocN(len * 3 * sizeof(float), "dlverts");
+ dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(dispbase, dl);
dl->col = nu->mat_nr;
@@ -1344,7 +1340,7 @@ static void fillBevelCap(Nurb *nu, DispList *dlb, float *prev_fp, ListBase *disp
DispList *dl;
dl = MEM_callocN(sizeof(DispList), "makeDispListbev2");
- dl->verts = MEM_mallocN(3 * sizeof(float) * dlb->nr, "dlverts");
+ dl->verts = MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts");
memcpy(dl->verts, prev_fp, 3 * sizeof(float) * dlb->nr);
dl->type = DL_POLY;
@@ -1425,7 +1421,7 @@ static void calc_bevfac_mapping(
bevp_array = MEM_mallocN(sizeof(*bevp_array) * (bl->nr - 1), "bevp_dists");
segments = MEM_callocN(sizeof(*segments) * segcount, "bevp_segmentlengths");
- bevp_prev = (BevPoint *)(bl + 1);
+ bevp_prev = bl->bevpoints;
bevp = bevp_prev + 1;
if (nu->type == CU_BEZIER) {
@@ -1626,7 +1622,7 @@ static void do_makeDispListCurveTypes(Scene *scene, Object *ob, ListBase *dispba
if (BLI_listbase_is_empty(&dlbev)) {
BevPoint *bevp;
dl = MEM_callocN(sizeof(DispList), "makeDispListbev");
- dl->verts = MEM_callocN(3 * sizeof(float) * bl->nr, "dlverts");
+ dl->verts = MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts");
BLI_addtail(dispbase, dl);
if (bl->poly != -1) dl->type = DL_POLY;
@@ -1644,7 +1640,7 @@ static void do_makeDispListCurveTypes(Scene *scene, Object *ob, ListBase *dispba
dl->rt = nu->flag & ~CU_2D;
a = dl->nr;
- bevp = (BevPoint *)(bl + 1);
+ bevp = bl->bevpoints;
data = dl->verts;
while (a--) {
data[0] = bevp->vec[0] + widfac * bevp->sina;
@@ -1668,6 +1664,10 @@ static void do_makeDispListCurveTypes(Scene *scene, Object *ob, ListBase *dispba
&start, &firstblend, &steps, &lastblend);
}
else {
+ if (fabsf(cu->bevfac2 - cu->bevfac1) < FLT_EPSILON) {
+ continue;
+ }
+
calc_bevfac_mapping(cu, bl, nu, use_render_resolution,
&start, &firstblend, &steps, &lastblend);
}
@@ -1678,7 +1678,7 @@ static void do_makeDispListCurveTypes(Scene *scene, Object *ob, ListBase *dispba
/* for each part of the bevel use a separate displblock */
dl = MEM_callocN(sizeof(DispList), "makeDispListbev1");
- dl->verts = data = MEM_callocN(3 * sizeof(float) * dlb->nr * steps, "dlverts");
+ dl->verts = data = MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts");
BLI_addtail(dispbase, dl);
dl->type = DL_SURF;
@@ -1696,13 +1696,13 @@ static void do_makeDispListCurveTypes(Scene *scene, Object *ob, ListBase *dispba
/* CU_2D conflicts with R_NOPUNOFLIP */
dl->rt = nu->flag & ~CU_2D;
- dl->bevelSplitFlag = MEM_callocN(sizeof(*dl->col2) * ((steps + 0x1F) >> 5),
+ dl->bevelSplitFlag = MEM_callocN(sizeof(*dl->bevelSplitFlag) * ((steps + 0x1F) >> 5),
"bevelSplitFlag");
/* for each point of poly make a bevel piece */
- bevp_first = (BevPoint *)(bl + 1);
- bevp_last = (BevPoint *)(bl + 1) + (bl->nr - 1);
- bevp = (BevPoint *)(bl + 1) + start;
+ bevp_first = bl->bevpoints;
+ bevp_last = &bl->bevpoints[bl->nr - 1];
+ bevp = &bl->bevpoints[start];
for (i = start, a = 0; a < steps; i++, bevp++, a++) {
float fac = 1.0;
float *cur_data = data;
diff --git a/source/blender/blenkernel/intern/editderivedmesh.c b/source/blender/blenkernel/intern/editderivedmesh.c
index f23d27d40d7..e74adea529b 100644
--- a/source/blender/blenkernel/intern/editderivedmesh.c
+++ b/source/blender/blenkernel/intern/editderivedmesh.c
@@ -2282,8 +2282,8 @@ static void cage_mapped_verts_callback(void *userData, int index, const float co
{
struct CageUserData *data = userData;
- if ((index >= 0 && index < data->totvert) && (!BLI_BITMAP_GET(data->visit_bitmap, index))) {
- BLI_BITMAP_SET(data->visit_bitmap, index);
+ if ((index >= 0 && index < data->totvert) && (!BLI_BITMAP_TEST(data->visit_bitmap, index))) {
+ BLI_BITMAP_ENABLE(data->visit_bitmap, index);
copy_v3_v3(data->cos_cage[index], co);
}
}
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c
index 65b9d2159df..a8de9b69fbe 100644
--- a/source/blender/blenkernel/intern/fcurve.c
+++ b/source/blender/blenkernel/intern/fcurve.c
@@ -469,7 +469,7 @@ static short get_fcurve_end_keyframes(FCurve *fcu, BezTriple **first, BezTriple
}
/* find last selected */
- bezt = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, sizeof(BezTriple), fcu->totvert);
+ bezt = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert);
for (i = 0; i < fcu->totvert; bezt--, i++) {
if (BEZSELECTED(bezt)) {
*last = bezt;
@@ -481,7 +481,7 @@ static short get_fcurve_end_keyframes(FCurve *fcu, BezTriple **first, BezTriple
else {
/* just full array */
*first = fcu->bezt;
- *last = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, sizeof(BezTriple), fcu->totvert);
+ *last = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert);
found = true;
}
@@ -2050,8 +2050,14 @@ static float fcurve_eval_keyframes(FCurve *fcu, BezTriple *bezts, float evaltime
/* evaltime occurs somewhere in the middle of the curve */
bool exact = false;
- /* - use binary search to find appropriate keyframes */
- a = binarysearch_bezt_index_ex(bezts, evaltime, fcu->totvert, 0.001, &exact);
+ /* Use binary search to find appropriate keyframes...
+ *
+ * The threshold here has the following constraints:
+ * - 0.001 is too coarse -> We get artifacts with 2cm driver movements at 1BU = 1m (see T40332)
+ * - 0.00001 is too fine -> Weird errors, like selecting the wrong keyframe range (see T39207), occur.
+ * This lower bound was established in b888a32eee8147b028464336ad2404d8155c64dd
+ */
+ a = binarysearch_bezt_index_ex(bezts, evaltime, fcu->totvert, 0.0001, &exact);
if (G.debug & G_DEBUG) printf("eval fcurve '%s' - %f => %d/%d, %d\n", fcu->rna_path, evaltime, a, fcu->totvert, exact);
if (exact) {
diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c
index f12a0720692..3188676fb6c 100644
--- a/source/blender/blenkernel/intern/idprop.c
+++ b/source/blender/blenkernel/intern/idprop.c
@@ -821,7 +821,7 @@ bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is
case IDP_INT:
return (IDP_Int(prop1) == IDP_Int(prop2));
case IDP_FLOAT:
-#ifdef DEBUG
+#if defined(DEBUG) && defined(WITH_PYTHON)
{
float p1 = IDP_Float(prop1);
float p2 = IDP_Float(prop2);
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index 83ea52a8938..db5c212d1db 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -2023,7 +2023,7 @@ int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf)
return(ok);
}
-/* same as BKE_imbuf_write() but crappy workaround not to perminantly modify
+/* same as BKE_imbuf_write() but crappy workaround not to permanently modify
* _some_, values in the imbuf */
int BKE_imbuf_write_as(ImBuf *ibuf, const char *name, ImageFormatData *imf,
const bool save_copy)
diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c
index 51fbb86b38e..ea55f32ea05 100644
--- a/source/blender/blenkernel/intern/lattice.c
+++ b/source/blender/blenkernel/intern/lattice.c
@@ -120,11 +120,11 @@ void BKE_lattice_bitmap_from_flag(Lattice *lt, BLI_bitmap *bitmap, const short f
bp = lt->def;
for (i = 0; i < tot; i++, bp++) {
if ((bp->f1 & flag) && (!respecthide || !bp->hide)) {
- BLI_BITMAP_SET(bitmap, i);
+ BLI_BITMAP_ENABLE(bitmap, i);
}
else {
if (clear) {
- BLI_BITMAP_CLEAR(bitmap, i);
+ BLI_BITMAP_DISABLE(bitmap, i);
}
}
}
@@ -626,16 +626,19 @@ static bool calc_curve_deform(Scene *scene, Object *par, float co[3],
short index;
const bool is_neg_axis = (axis > 2);
- /* to be sure, mostly after file load */
- if (ELEM(NULL, par->curve_cache, par->curve_cache->path)) {
+ /* to be sure, mostly after file load, also cyclic dependencies */
#ifdef CYCLIC_DEPENDENCY_WORKAROUND
+ if (par->curve_cache == NULL) {
BKE_displist_make_curveTypes(scene, par, false);
+ }
#endif
- if (par->curve_cache->path == NULL) {
- return 0; // happens on append and cyclic dependencies...
- }
+
+ if (par->curve_cache->path == NULL) {
+ return 0; /* happens on append, cyclic dependencies
+ * and empty curves
+ */
}
-
+
/* options */
if (is_neg_axis) {
index = axis - 3;
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index fe2d1481581..c8173ae0513 100644
--- a/source/blender/blenkernel/intern/library.c
+++ b/source/blender/blenkernel/intern/library.c
@@ -71,6 +71,7 @@
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
+#include "BLI_threads.h"
#include "BLF_translation.h"
#include "BKE_action.h"
@@ -747,12 +748,14 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name)
id = alloc_libblock_notest(type);
if (id) {
+ BKE_main_lock(bmain);
BLI_addtail(lb, id);
id->us = 1;
id->icon_id = 0;
*( (short *)id->name) = type;
new_id(lb, id, name);
/* alphabetic insertion: is in new_id */
+ BKE_main_unlock(bmain);
}
DAG_id_type_tag(bmain, type);
return id;
@@ -881,10 +884,8 @@ static void animdata_dtar_clear_cb(ID *UNUSED(id), AnimData *adt, void *userdata
}
}
-void BKE_libblock_free_data(ID *id)
+void BKE_libblock_free_data(Main *bmain, ID *id)
{
- Main *bmain = G.main; /* should eventually be an arg */
-
if (id->properties) {
IDP_FreeProperty(id->properties);
MEM_freeN(id->properties);
@@ -1008,12 +1009,15 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, bool do_id_user)
}
/* avoid notifying on removed data */
+ BKE_main_lock(bmain);
+
if (free_notifier_reference_cb)
free_notifier_reference_cb(id);
BLI_remlink(lb, id);
- BKE_libblock_free_data(id);
+ BKE_libblock_free_data(bmain, id);
+ BKE_main_unlock(bmain);
MEM_freeN(id);
}
@@ -1043,7 +1047,10 @@ void BKE_libblock_free_us(Main *bmain, void *idv) /* test users */
Main *BKE_main_new(void)
{
Main *bmain = MEM_callocN(sizeof(Main), "new main");
- bmain->eval_ctx = MEM_callocN(sizeof(EvaluationContext), "EvaluationContext");
+ bmain->eval_ctx = MEM_callocN(sizeof(EvaluationContext),
+ "EvaluationContext");
+ bmain->lock = MEM_mallocN(sizeof(SpinLock), "main lock");
+ BLI_spin_init((SpinLock *)bmain->lock);
return bmain;
}
@@ -1106,10 +1113,22 @@ void BKE_main_free(Main *mainvar)
}
}
+ BLI_spin_end((SpinLock *)mainvar->lock);
+ MEM_freeN(mainvar->lock);
MEM_freeN(mainvar->eval_ctx);
MEM_freeN(mainvar);
}
+void BKE_main_lock(struct Main *bmain)
+{
+ BLI_spin_lock((SpinLock *) bmain->lock);
+}
+
+void BKE_main_unlock(struct Main *bmain)
+{
+ BLI_spin_unlock((SpinLock *) bmain->lock);
+}
+
/* ***************** ID ************************ */
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index de3aea9267f..971db1997de 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -780,11 +780,13 @@ void test_object_materials(Main *bmain, ID *id)
return;
}
+ BKE_main_lock(bmain);
for (ob = bmain->object.first; ob; ob = ob->id.next) {
if (ob->data == id) {
BKE_material_resize_object(ob, *totcol, false);
}
}
+ BKE_main_unlock(bmain);
}
void assign_material_id(ID *id, Material *ma, short act)
diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c
index fc6f9149841..47aeb18ff36 100644
--- a/source/blender/blenkernel/intern/mball.c
+++ b/source/blender/blenkernel/intern/mball.c
@@ -2361,8 +2361,8 @@ void BKE_mball_polygonize(EvaluationContext *eval_ctx, Scene *scene, Object *ob,
process.indices = NULL;
a = process.vertices.count;
- dl->verts = co = MEM_mallocN(sizeof(float) * 3 * a, "mballverts");
- dl->nors = no = MEM_mallocN(sizeof(float) * 3 * a, "mballnors");
+ dl->verts = co = MEM_mallocN(sizeof(float[3]) * a, "mballverts");
+ dl->nors = no = MEM_mallocN(sizeof(float[3]) * a, "mballnors");
for (a = 0; a < process.vertices.count; ptr++, a++, no += 3, co += 3) {
copy_v3_v3(co, ptr->co);
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index f0566a7f473..7c6164a8174 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -1531,7 +1531,7 @@ void BKE_mesh_from_nurbs(Object *ob)
}
typedef struct EdgeLink {
- Link *next, *prev;
+ struct EdgeLink *next, *prev;
void *edge;
} EdgeLink;
@@ -1610,13 +1610,11 @@ void BKE_mesh_to_curve_nurblist(DerivedMesh *dm, ListBase *nurblist, const int e
BLI_freelinkN(&edges, edges.last); totedges--;
while (ok) { /* while connected edges are found... */
+ EdgeLink *edl = edges.last;
ok = false;
- i = totedges;
- while (i) {
- EdgeLink *edl;
+ while (edl) {
+ EdgeLink *edl_prev = edl->prev;
- i -= 1;
- edl = BLI_findlink(&edges, i);
med = edl->edge;
if (med->v1 == endVert) {
@@ -1643,6 +1641,8 @@ void BKE_mesh_to_curve_nurblist(DerivedMesh *dm, ListBase *nurblist, const int e
BLI_freelinkN(&edges, edl); totedges--;
ok = true;
}
+
+ edl = edl_prev;
}
}
@@ -2148,7 +2148,7 @@ Mesh *BKE_mesh_new_from_object(
* if it didn't the curve did not have any segments or otherwise
* would have generated an empty mesh */
if (tmpobj->type != OB_MESH) {
- BKE_libblock_free_us(G.main, tmpobj);
+ BKE_libblock_free_us(bmain, tmpobj);
return NULL;
}
diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c
index b090e770e5e..cb0386b1203 100644
--- a/source/blender/blenkernel/intern/mesh_evaluate.c
+++ b/source/blender/blenkernel/intern/mesh_evaluate.c
@@ -1021,7 +1021,7 @@ void BKE_mesh_poly_edgebitmap_insert(unsigned int *edge_bitmap, const MPoly *mp,
ml = mloop;
while (i-- != 0) {
- BLI_BITMAP_SET(edge_bitmap, ml->e);
+ BLI_BITMAP_ENABLE(edge_bitmap, ml->e);
ml++;
}
}
@@ -1411,7 +1411,7 @@ int BKE_mesh_recalc_tessellation(CustomData *fdata, CustomData *ldata, CustomDat
mul_v2_m3v3(projverts[j], axis_mat, mvert[ml->v].co);
}
- BLI_polyfill_calc_arena((const float (*)[2])projverts, mp_totloop, tris, arena);
+ BLI_polyfill_calc_arena((const float (*)[2])projverts, mp_totloop, -1, tris, arena);
/* apply fill */
for (j = 0; j < totfilltri; j++) {
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index b5cbec2870b..1c42603cf4a 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -709,7 +709,7 @@ const char *modifier_path_relbase(Object *ob)
else {
/* last resort, better then using "" which resolves to the current
* working directory */
- return BLI_temporary_dir();
+ return BLI_temp_dir_session();
}
}
@@ -719,7 +719,7 @@ void modifier_path_init(char *path, int path_maxlen, const char *name)
/* elubie: changed this to default to the same dir as the render output
* to prevent saving to C:\ on Windows */
BLI_join_dirfile(path, path_maxlen,
- G.relbase_valid ? "//" : BLI_temporary_dir(),
+ G.relbase_valid ? "//" : BLI_temp_dir_session(),
name);
}
diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c
index 3d8e35699a9..1d5bbbe1aeb 100644
--- a/source/blender/blenkernel/intern/multires.c
+++ b/source/blender/blenkernel/intern/multires.c
@@ -134,7 +134,7 @@ static BLI_bitmap *multires_mdisps_upsample_hidden(BLI_bitmap *lo_hidden,
/* low-res blocks */
for (yl = 0; yl < lo_gridsize; yl++) {
for (xl = 0; xl < lo_gridsize; xl++) {
- int lo_val = BLI_BITMAP_GET(lo_hidden, yl * lo_gridsize + xl);
+ int lo_val = BLI_BITMAP_TEST(lo_hidden, yl * lo_gridsize + xl);
/* high-res blocks */
for (yo = -offset; yo <= offset; yo++) {
@@ -153,13 +153,15 @@ static BLI_bitmap *multires_mdisps_upsample_hidden(BLI_bitmap *lo_hidden,
/* If prev_hidden is available, copy it to
* subd, except when the equivalent element in
* lo_hidden is different */
- if (lo_val != prev_hidden[hi_ndx])
- BLI_BITMAP_MODIFY(subd, hi_ndx, lo_val);
- else
- BLI_BITMAP_MODIFY(subd, hi_ndx, prev_hidden[hi_ndx]);
+ if (lo_val != prev_hidden[hi_ndx]) {
+ BLI_BITMAP_SET(subd, hi_ndx, lo_val);
+ }
+ else {
+ BLI_BITMAP_SET(subd, hi_ndx, prev_hidden[hi_ndx]);
+ }
}
else {
- BLI_BITMAP_MODIFY(subd, hi_ndx, lo_val);
+ BLI_BITMAP_SET(subd, hi_ndx, lo_val);
}
}
}
@@ -187,10 +189,10 @@ static BLI_bitmap *multires_mdisps_downsample_hidden(BLI_bitmap *old_hidden,
for (y = 0; y < new_gridsize; y++) {
for (x = 0; x < new_gridsize; x++) {
- old_value = BLI_BITMAP_GET(old_hidden,
+ old_value = BLI_BITMAP_TEST(old_hidden,
factor * y * old_gridsize + x * factor);
- BLI_BITMAP_MODIFY(new_hidden, y * new_gridsize + x, old_value);
+ BLI_BITMAP_SET(new_hidden, y * new_gridsize + x, old_value);
}
}
@@ -272,7 +274,7 @@ static MDisps *multires_mdisps_initialize_hidden(Mesh *me, int level)
md->hidden = BLI_BITMAP_NEW(gridarea, "MDisps.hidden initialize");
for (k = 0; k < gridarea; k++)
- BLI_BITMAP_SET(md->hidden, k);
+ BLI_BITMAP_ENABLE(md->hidden, k);
}
}
@@ -286,7 +288,7 @@ DerivedMesh *get_multires_dm(Scene *scene, MultiresModifierData *mmd, Object *ob
DerivedMesh *tdm = mesh_get_derived_deform(scene, ob, CD_MASK_BAREMESH);
DerivedMesh *dm;
- dm = mti->applyModifier(md, ob, tdm, MOD_APPLY_USECACHE);
+ dm = mti->applyModifier(md, ob, tdm, MOD_APPLY_USECACHE | MOD_APPLY_IGNORE_SIMPLIFY);
if (dm == tdm) {
dm = CDDM_copy(tdm);
}
@@ -338,12 +340,15 @@ MultiresModifierData *get_multires_modifier(Scene *scene, Object *ob, bool use_f
return mmd;
}
-static int multires_get_level(Object *ob, MultiresModifierData *mmd, int render)
+static int multires_get_level(Object *ob, MultiresModifierData *mmd,
+ bool render, bool ignore_simplify)
{
if (render)
return (mmd->modifier.scene) ? get_render_subsurf_level(&mmd->modifier.scene->r, mmd->renderlvl) : mmd->renderlvl;
else if (ob->mode == OB_MODE_SCULPT)
return mmd->sculptlvl;
+ else if (ignore_simplify)
+ return mmd->lvl;
else
return (mmd->modifier.scene) ? get_render_subsurf_level(&mmd->modifier.scene->r, mmd->lvl) : mmd->lvl;
}
@@ -433,7 +438,7 @@ int multiresModifier_reshapeFromDeformMod(Scene *scene, MultiresModifierData *mm
int numVerts, result;
float (*deformedVerts)[3];
- if (multires_get_level(ob, mmd, 0) == 0)
+ if (multires_get_level(ob, mmd, false, true) == 0)
return 0;
/* Create DerivedMesh for deformation modifier */
@@ -682,7 +687,7 @@ static void multires_del_higher(MultiresModifierData *mmd, Object *ob, int lvl)
void multiresModifier_del_levels(MultiresModifierData *mmd, Object *ob, int direction)
{
Mesh *me = BKE_mesh_from_object(ob);
- int lvl = multires_get_level(ob, mmd, 0);
+ int lvl = multires_get_level(ob, mmd, false, true);
int levels = mmd->totlvl - lvl;
MDisps *mdisps;
@@ -1431,7 +1436,9 @@ DerivedMesh *multires_make_derived_from_derived(DerivedMesh *dm,
CCGDerivedMesh *ccgdm = NULL;
CCGElem **gridData, **subGridData;
CCGKey key;
- int lvl = multires_get_level(ob, mmd, (flags & MULTIRES_USE_RENDER_PARAMS));
+ const bool render = (flags & MULTIRES_USE_RENDER_PARAMS) != 0;
+ const bool ignore_simplify = (flags & MULTIRES_IGNORE_SIMPLIFY) != 0;
+ int lvl = multires_get_level(ob, mmd, render, ignore_simplify);
int i, gridSize, numGrids;
if (lvl == 0)
diff --git a/source/blender/blenkernel/intern/navmesh_conversion.c b/source/blender/blenkernel/intern/navmesh_conversion.c
index 64d59b165e1..6c3f4d545d3 100644
--- a/source/blender/blenkernel/intern/navmesh_conversion.c
+++ b/source/blender/blenkernel/intern/navmesh_conversion.c
@@ -305,7 +305,7 @@ struct SortContext {
const int *trisToFacesMap;
};
-static int compareByData(void *ctx, const void *a, const void *b)
+static int compareByData(const void *a, const void *b, void *ctx)
{
return (((struct SortContext *)ctx)->recastData[((struct SortContext *)ctx)->trisToFacesMap[*(int *)a]] -
((struct SortContext *)ctx)->recastData[((struct SortContext *)ctx)->trisToFacesMap[*(int *)b]]);
@@ -341,7 +341,7 @@ int buildNavMeshData(const int nverts, const float *verts,
trisMapping[i] = i;
context.recastData = recastData;
context.trisToFacesMap = trisToFacesMap;
- BLI_qsort_r(trisMapping, ntris, sizeof(int), &context, compareByData);
+ BLI_qsort_r(trisMapping, ntris, sizeof(int), compareByData, &context);
/* search first valid triangle - triangle of convex polygon */
validTriStart = -1;
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index c11611a1f65..56a76a54955 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -1777,7 +1777,7 @@ void ntreeFreeTree_ex(bNodeTree *ntree, const bool do_id_user)
if (tntree == ntree)
break;
if (tntree == NULL) {
- BKE_libblock_free_data(&ntree->id);
+ BKE_libblock_free_data(G.main, &ntree->id);
}
}
/* same as ntreeFreeTree_ex but always manage users */
@@ -3495,6 +3495,8 @@ static void registerShaderNodes(void)
register_node_type_sh_combrgb();
register_node_type_sh_sephsv();
register_node_type_sh_combhsv();
+ register_node_type_sh_sepxyz();
+ register_node_type_sh_combxyz();
register_node_type_sh_hue_sat();
register_node_type_sh_attribute();
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 745088aced2..f72fd1f645d 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -1083,6 +1083,9 @@ void BKE_object_lod_sort(Object *ob)
bool BKE_object_lod_remove(Object *ob, int level)
{
LodLevel *rem;
+
+ if (ob == NULL)
+ return false;
if (level < 1 || level > BLI_countlist(&ob->lodlevels) - 1)
return false;
@@ -1479,7 +1482,7 @@ Object *BKE_object_copy_ex(Main *bmain, Object *ob, bool copy_caches)
defgroup_copy_list(&obn->defbase, &ob->defbase);
BKE_constraints_copy(&obn->constraints, &ob->constraints, true);
- obn->mode = 0;
+ obn->mode = OB_MODE_OBJECT;
obn->sculpt = NULL;
/* increase user numbers */
@@ -2975,7 +2978,7 @@ void BKE_object_handle_update_ex(EvaluationContext *eval_ctx,
lamp_drivers_update(scene, ob->data, ctime);
/* particles */
- if (ob->particlesystem.first) {
+ if (ob != scene->obedit && ob->particlesystem.first) {
ParticleSystem *tpsys, *psys;
DerivedMesh *dm;
ob->transflag &= ~OB_DUPLIPARTS;
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index 24616d88852..9a144b5461a 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -347,10 +347,10 @@ bool paint_is_grid_face_hidden(const unsigned int *grid_hidden,
int gridsize, int x, int y)
{
/* skip face if any of its corners are hidden */
- return (BLI_BITMAP_GET(grid_hidden, y * gridsize + x) ||
- BLI_BITMAP_GET(grid_hidden, y * gridsize + x + 1) ||
- BLI_BITMAP_GET(grid_hidden, (y + 1) * gridsize + x + 1) ||
- BLI_BITMAP_GET(grid_hidden, (y + 1) * gridsize + x));
+ return (BLI_BITMAP_TEST(grid_hidden, y * gridsize + x) ||
+ BLI_BITMAP_TEST(grid_hidden, y * gridsize + x + 1) ||
+ BLI_BITMAP_TEST(grid_hidden, (y + 1) * gridsize + x + 1) ||
+ BLI_BITMAP_TEST(grid_hidden, (y + 1) * gridsize + x));
}
/* Return true if all vertices in the face are visible, false otherwise */
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
index 665bf2a85a9..221bc2689bb 100644
--- a/source/blender/blenkernel/intern/particle_system.c
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -1011,7 +1011,7 @@ static void *distribute_threads_exec_cb(void *data)
return 0;
}
-static int distribute_compare_orig_index(void *user_data, const void *p1, const void *p2)
+static int distribute_compare_orig_index(const void *p1, const void *p2, void *user_data)
{
int *orig_index = (int *) user_data;
int index1 = orig_index[*(const int *)p1];
@@ -1344,7 +1344,7 @@ static int distribute_threads_init_data(ParticleThread *threads, Scene *scene, D
}
if (orig_index) {
- BLI_qsort_r(particle_element, totpart, sizeof(int), orig_index, distribute_compare_orig_index);
+ BLI_qsort_r(particle_element, totpart, sizeof(int), distribute_compare_orig_index, orig_index);
}
}
diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c
index 3cb5f736cab..1a815cf0dee 100644
--- a/source/blender/blenkernel/intern/pbvh.c
+++ b/source/blender/blenkernel/intern/pbvh.c
@@ -251,12 +251,12 @@ static int map_insert_vert(PBVH *bvh, GHash *map,
if (value_p == NULL) {
void *value;
- if (BLI_BITMAP_GET(bvh->vert_bitmap, vertex)) {
+ if (BLI_BITMAP_TEST(bvh->vert_bitmap, vertex)) {
value = SET_INT_IN_POINTER(~(*face_verts));
++(*face_verts);
}
else {
- BLI_BITMAP_SET(bvh->vert_bitmap, vertex);
+ BLI_BITMAP_ENABLE(bvh->vert_bitmap, vertex);
value = SET_INT_IN_POINTER(*uniq_verts);
++(*uniq_verts);
}
@@ -410,7 +410,7 @@ static void build_leaf(PBVH *bvh, int node_index, BBC *prim_bbc,
}
/* Return zero if all primitives in the node can be drawn with the
- * same material (including flat/smooth shading), non-zerootherwise */
+ * same material (including flat/smooth shading), non-zero otherwise */
static int leaf_needs_material_split(PBVH *bvh, int offset, int count)
{
int i, prim;
diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c
index 83e0d1a4740..1218e3a063c 100644
--- a/source/blender/blenkernel/intern/pbvh_bmesh.c
+++ b/source/blender/blenkernel/intern/pbvh_bmesh.c
@@ -71,7 +71,7 @@ static void pbvh_bmesh_node_finalize(PBVH *bvh, int node_index, const int cd_ver
v = l_iter->v;
if (!BLI_gset_haskey(n->bm_unique_verts, v)) {
if (BM_ELEM_CD_GET_INT(v, cd_vert_node_offset) != DYNTOPO_NODE_NONE) {
- BLI_gset_reinsert(n->bm_other_verts, v, NULL);
+ BLI_gset_add(n->bm_other_verts, v);
}
else {
BLI_gset_insert(n->bm_unique_verts, v);
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index c8042171a94..063a81e6efb 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -51,7 +51,6 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BLI_system.h"
-#include BLI_SYSTEM_PID_H
#include "BLF_translation.h"
@@ -96,7 +95,6 @@
#endif
/* needed for directory lookup */
-/* untitled blend's need getpid for a unique name */
#ifndef WIN32
# include <dirent.h>
#else
@@ -1466,7 +1464,7 @@ static int ptcache_path(PTCacheID *pid, char *filename)
/* use the temp path. this is weak but better then not using point cache at all */
/* temporary directory is assumed to exist and ALWAYS has a trailing slash */
- BLI_snprintf(filename, MAX_PTCACHE_PATH, "%s"PTCACHE_PATH"%d", BLI_temporary_dir(), abs(getpid()));
+ BLI_snprintf(filename, MAX_PTCACHE_PATH, "%s"PTCACHE_PATH, BLI_temp_dir_session());
return BLI_add_slash(filename); /* new strlen() */
}
diff --git a/source/blender/blenkernel/intern/sca.c b/source/blender/blenkernel/intern/sca.c
index b0b64cac802..1310162483e 100644
--- a/source/blender/blenkernel/intern/sca.c
+++ b/source/blender/blenkernel/intern/sca.c
@@ -391,6 +391,7 @@ void init_actuator(bActuator *act)
bSoundActuator *sa;
bSteeringActuator *sta;
bArmatureActuator *arma;
+ bMouseActuator *ma;
if (act->data) MEM_freeN(act->data);
act->data= NULL;
@@ -477,6 +478,15 @@ void init_actuator(bActuator *act)
sta->flag = ACT_STEERING_AUTOMATICFACING;
sta->facingaxis = 1;
break;
+ case ACT_MOUSE:
+ ma = act->data = MEM_callocN(sizeof( bMouseActuator ), "mouse act");
+ ma->flag = ACT_MOUSE_VISIBLE|ACT_MOUSE_USE_AXIS_X|ACT_MOUSE_USE_AXIS_Y|ACT_MOUSE_RESET_X|ACT_MOUSE_RESET_Y|ACT_MOUSE_LOCAL_Y;
+ ma->sensitivity[0] = ma->sensitivity[1] = 2.f;
+ ma->object_axis[0] = ACT_MOUSE_OBJECT_AXIS_Z;
+ ma->object_axis[1] = ACT_MOUSE_OBJECT_AXIS_X;
+ ma->limit_y[0] = DEG2RADF(-90.0f);
+ ma->limit_y[1] = DEG2RADF(90.0f);
+ break;
default:
; /* this is very severe... I cannot make any memory for this */
/* logic brick... */
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index c94d0dfa548..f8ce31f1b8a 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -1462,7 +1462,7 @@ static void scene_update_objects(EvaluationContext *eval_ctx, Main *bmain, Scene
bool need_singlethread_pass;
/* Early check for whether we need to invoke all the task-based
- * tihngs (spawn new ppol, traverse dependency graph and so on).
+ * things (spawn new ppol, traverse dependency graph and so on).
*
* Basically if there's no ID datablocks tagged for update which
* corresponds to object->recalc flags (which are checked in
diff --git a/source/blender/blenkernel/intern/seqeffects.c b/source/blender/blenkernel/intern/seqeffects.c
index 7ed9af8b87f..2b14b92217b 100644
--- a/source/blender/blenkernel/intern/seqeffects.c
+++ b/source/blender/blenkernel/intern/seqeffects.c
@@ -1398,7 +1398,7 @@ static float check_zone(WipeZone *wipezone, int x, int y, Sequence *seq, float f
x = x - halfx;
y = y - halfy;
- temp2 = asin(abs(y) / sqrt(x * x + y * y));
+ temp2 = asin(abs(y) / hypot(x, y));
if (x <= 0 && y >= 0) temp2 = (float)M_PI - temp2;
else if (x <= 0 && y <= 0) temp2 += (float)M_PI;
else if (x >= 0 && y <= 0) temp2 = 2.0f * (float)M_PI - temp2;
@@ -1441,7 +1441,7 @@ static float check_zone(WipeZone *wipezone, int x, int y, Sequence *seq, float f
temp1 = xo * (1 - facf0 / 2) - xo * facf0 / 2;
temp2 = yo * (1 - facf0 / 2) - yo * facf0 / 2;
- pointdist = sqrtf(temp1 * temp1 + temp2 * temp2);
+ pointdist = hypot(temp1, temp2);
if (b2 < b1 && b2 < b3) {
if (hwidth < pointdist)
@@ -1498,9 +1498,9 @@ static float check_zone(WipeZone *wipezone, int x, int y, Sequence *seq, float f
hwidth = width * 0.5f;
temp1 = (halfx - (halfx) * facf0);
- pointdist = sqrtf(temp1 * temp1 + temp1 * temp1);
+ pointdist = hypotf(temp1, temp1);
- temp2 = sqrtf((halfx - x) * (halfx - x) + (halfy - y) * (halfy - y));
+ temp2 = hypotf(halfx - x, halfy - y);
if (temp2 > pointdist) output = in_band(hwidth, fabsf(temp2 - pointdist), 0, 1);
else output = in_band(hwidth, fabsf(temp2 - pointdist), 1, 1);
diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c
index a1cd6b87a05..2b386708cf8 100644
--- a/source/blender/blenkernel/intern/sequencer.c
+++ b/source/blender/blenkernel/intern/sequencer.c
@@ -2813,8 +2813,7 @@ static ImBuf *seq_render_strip(const SeqRenderData *context, Sequence *seq, floa
ibuf = BKE_sequencer_cache_get(context, seq, cfra, SEQ_STRIPELEM_IBUF);
if (ibuf == NULL) {
- if (ibuf == NULL)
- ibuf = copy_from_ibuf_still(context, seq, nr);
+ ibuf = copy_from_ibuf_still(context, seq, nr);
if (ibuf == NULL) {
ibuf = BKE_sequencer_preprocessed_cache_get(context, seq, cfra, SEQ_STRIPELEM_IBUF);
@@ -3075,13 +3074,12 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, ListBase *seq
ImBuf *BKE_sequencer_give_ibuf(const SeqRenderData *context, float cfra, int chanshown)
{
Editing *ed = BKE_sequencer_editing_get(context->scene, false);
- int count;
ListBase *seqbasep;
if (ed == NULL) return NULL;
- count = BLI_countlist(&ed->metastack);
- if ((chanshown < 0) && (count > 0)) {
+ if ((chanshown < 0) && !BLI_listbase_is_empty(&ed->metastack)) {
+ int count = BLI_countlist(&ed->metastack);
count = max_ii(count + chanshown, 0);
seqbasep = ((MetaStack *)BLI_findlink(&ed->metastack, count))->oldbasep;
}
diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c
index d524c5dca17..90ac712e55a 100644
--- a/source/blender/blenkernel/intern/smoke.c
+++ b/source/blender/blenkernel/intern/smoke.c
@@ -205,7 +205,7 @@ void smoke_reallocate_highres_fluid(SmokeDomainSettings *sds, float dx, int res[
/* smoke_turbulence_init uses non-threadsafe functions from fftw3 lib (like fftw_plan & co). */
BLI_lock_thread(LOCK_FFTW);
- sds->wt = smoke_turbulence_init(res, sds->amplify + 1, sds->noise, BLI_temporary_dir(), use_fire, use_colors);
+ sds->wt = smoke_turbulence_init(res, sds->amplify + 1, sds->noise, BLI_temp_dir_session(), use_fire, use_colors);
BLI_unlock_thread(LOCK_FFTW);
diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c
index 8e4abe7f437..12a19381e9d 100644
--- a/source/blender/blenkernel/intern/subsurf_ccg.c
+++ b/source/blender/blenkernel/intern/subsurf_ccg.c
@@ -359,7 +359,7 @@ static int ss_sync_from_uv(CCGSubSurf *ss, CCGSubSurf *origss, DerivedMesh *dm,
MVert *mv0 = mvert + (ml[j_next].v);
MVert *mv1 = mvert + (ml[j].v);
- if (BLI_edgeset_reinsert(eset, v0, v1)) {
+ if (BLI_edgeset_add(eset, v0, v1)) {
CCGEdge *e, *orige = ccgSubSurf_getFaceEdge(origf, j_next);
CCGEdgeHDL ehdl = SET_INT_IN_POINTER(mp->loopstart + j_next);
float crease;
@@ -1076,7 +1076,7 @@ void subsurf_copy_grid_hidden(DerivedMesh *dm, const MPoly *mpoly,
vndx = getFaceIndex(ss, f, j, x, y, edgeSize, gridSize);
offset = (y * factor) * hidden_gridsize + (x * factor);
- if (BLI_BITMAP_GET(md->hidden, offset))
+ if (BLI_BITMAP_TEST(md->hidden, offset))
mvert[vndx].flag |= ME_HIDE;
}
}
@@ -1456,6 +1456,7 @@ static void ccgdm_getVertCos(DerivedMesh *dm, float (*cos)[3])
edgeMap2[GET_INT_FROM_POINTER(ccgSubSurf_getEdgeEdgeHandle(e))] = e;
}
+ ccgEdgeIterator_free(ei);
totface = ccgSubSurf_getNumFaces(ss);
faceMap2 = MEM_mallocN(totface * sizeof(*faceMap2), "facemap");
@@ -3541,6 +3542,7 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss,
ccgdm->edgeMap[GET_INT_FROM_POINTER(ccgSubSurf_getEdgeEdgeHandle(e))].edge = e;
}
+ ccgEdgeIterator_free(ei);
totface = ccgSubSurf_getNumFaces(ss);
ccgdm->faceMap = MEM_mallocN(totface * sizeof(*ccgdm->faceMap), "faceMap");
diff --git a/source/blender/blenkernel/intern/tracking_stabilize.c b/source/blender/blenkernel/intern/tracking_stabilize.c
index d5b3f3c0436..ffbdf5cb486 100644
--- a/source/blender/blenkernel/intern/tracking_stabilize.c
+++ b/source/blender/blenkernel/intern/tracking_stabilize.c
@@ -59,12 +59,11 @@ static bool stabilization_median_point_get(MovieTracking *tracking, int framenr,
track = tracking->tracks.first;
while (track) {
if (track->flag & TRACK_USE_2D_STAB) {
- MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, framenr);
+ MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
- if (marker != NULL && (marker->flag & MARKER_DISABLED) == 0) {
- minmax_v2v2_v2(min, max, marker->pos);
- ok = true;
- }
+ minmax_v2v2_v2(min, max, marker->pos);
+
+ ok = true;
}
track = track->next;
@@ -102,26 +101,22 @@ static void stabilization_calculate_data(MovieTracking *tracking, int framenr, i
float x0 = (float)width / 2.0f, y0 = (float)height / 2.0f;
float x = median[0] * width, y = median[1] * height;
- marker = BKE_tracking_marker_get_exact(stab->rot_track, 1);
- if (marker != NULL && (marker->flag & MARKER_DISABLED) == 0) {
- sub_v2_v2v2(a, marker->pos, firstmedian);
- a[0] *= width;
- a[1] *= height;
+ marker = BKE_tracking_marker_get(stab->rot_track, 1);
+ sub_v2_v2v2(a, marker->pos, firstmedian);
+ a[0] *= width;
+ a[1] *= height;
- marker = BKE_tracking_marker_get_exact(stab->rot_track, framenr);
- if (marker != NULL && (marker->flag & MARKER_DISABLED) == 0) {
- sub_v2_v2v2(b, marker->pos, median);
- b[0] *= width;
- b[1] *= height;
+ marker = BKE_tracking_marker_get(stab->rot_track, framenr);
+ sub_v2_v2v2(b, marker->pos, median);
+ b[0] *= width;
+ b[1] *= height;
- *angle = -atan2f(a[0] * b[1] - a[1] * b[0], a[0] * b[0] + a[1] * b[1]);
- *angle *= stab->rotinf;
+ *angle = -atan2f(a[0] * b[1] - a[1] * b[0], a[0] * b[0] + a[1] * b[1]);
+ *angle *= stab->rotinf;
- /* convert to rotation around image center */
- translation[0] -= (x0 + (x - x0) * cosf(*angle) - (y - y0) * sinf(*angle) - x) * (*scale);
- translation[1] -= (y0 + (x - x0) * sinf(*angle) + (y - y0) * cosf(*angle) - y) * (*scale);
- }
- }
+ /* convert to rotation around image center */
+ translation[0] -= (x0 + (x - x0) * cosf(*angle) - (y - y0) * sinf(*angle) - x) * (*scale);
+ translation[1] -= (y0 + (x - x0) * sinf(*angle) + (y - y0) * cosf(*angle) - y) * (*scale);
}
}
@@ -160,7 +155,7 @@ static float stabilization_calculate_autoscale_factor(MovieTracking *tracking, i
}
/* For every frame we calculate scale factor needed to eliminate black
- * aread and choose largest scale factor as final one.
+ * area and choose largest scale factor as final one.
*/
for (cfra = sfra; cfra <= efra; cfra++) {
float median[2];
@@ -170,9 +165,7 @@ static float stabilization_calculate_autoscale_factor(MovieTracking *tracking, i
float points[4][2] = {{0.0f, 0.0f}, {0.0f, height}, {width, height}, {width, 0.0f}};
float si, co;
- if (!stabilization_median_point_get(tracking, cfra, median)) {
- continue;
- }
+ stabilization_median_point_get(tracking, cfra, median);
stabilization_calculate_data(tracking, cfra, width, height, firstmedian, median, translation,
&tmp_scale, &angle);
@@ -281,9 +274,9 @@ void BKE_tracking_stabilization_data_get(MovieTracking *tracking, int framenr, i
* However, it's still better to replace this with real first
* frame number at which tracks are appearing.
*/
- if (stabilization_median_point_get(tracking, 1, firstmedian) &&
- stabilization_median_point_get(tracking, framenr, median))
- {
+ if (stabilization_median_point_get(tracking, 1, firstmedian)) {
+ stabilization_median_point_get(tracking, framenr, median);
+
if ((stab->flag & TRACKING_AUTOSCALE) == 0)
stab->scale = 1.0f;
diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c
index de6424f3145..8aca9f78702 100644
--- a/source/blender/blenkernel/intern/unit.c
+++ b/source/blender/blenkernel/intern/unit.c
@@ -536,7 +536,7 @@ static int unit_scale_str(char *str, int len_max, char *str_tmp, double scale_pr
len_name = strlen(replace_str);
len_move = (len - (found_ofs + len_name)) + 1; /* 1+ to copy the string terminator */
- len_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "*%g"SEP_STR, unit->scalar / scale_pref); /* # removed later */
+ len_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "*%.9g"SEP_STR, unit->scalar / scale_pref); /* # removed later */
if (len_num > len_max)
len_num = len_max;
@@ -598,7 +598,7 @@ static int unit_find(const char *str, bUnitDef *unit)
* ...will be resolved by python.
*
* values will be split by a comma's
- * 5'2" -> 5'0.0254, 2*0.3048
+ * 5'2" -> 5*0.3048, 2*0.0254
*
* str_prev is optional, when valid it is used to get a base unit when none is set.
*
diff --git a/source/blender/blenlib/BLI_array.h b/source/blender/blenlib/BLI_array.h
index 4110c85c2a5..f8e23629429 100644
--- a/source/blender/blenlib/BLI_array.h
+++ b/source/blender/blenlib/BLI_array.h
@@ -75,10 +75,10 @@ void _bli_array_grow_func(void **arr_p, const void *arr_static,
* switching to dynamic heap allocation */
#define BLI_array_staticdeclare(arr, maxstatic) \
int _##arr##_count = 0; \
- char _##arr##_static[maxstatic * sizeof(arr)]
+ char _##arr##_static[maxstatic * sizeof(*(arr))]
/* this returns the logical size of the array, not including buffering. */
-#define BLI_array_count(arr) _##arr##_count
+#define BLI_array_count(arr) ((void)0, _##arr##_count)
/* Grow the array by a fixed number of items.
*
diff --git a/source/blender/blenlib/BLI_bitmap.h b/source/blender/blenlib/BLI_bitmap.h
index 3d56156bfc1..5431785aa84 100644
--- a/source/blender/blenlib/BLI_bitmap.h
+++ b/source/blender/blenlib/BLI_bitmap.h
@@ -37,17 +37,17 @@ typedef unsigned int BLI_bitmap;
/* internal use */
/* 2^5 = 32 (bits) */
-#define BLI_BITMAP_POWER 5
+#define _BITMAP_POWER 5
/* 0b11111 */
-#define BLI_BITMAP_MASK 31
+#define _BITMAP_MASK 31
/* number of blocks needed to hold '_tot' bits */
-#define BLI_BITMAP_NUM_BLOCKS(_tot) \
- (((_tot) >> BLI_BITMAP_POWER) + 1)
+#define _BITMAP_NUM_BLOCKS(_tot) \
+ (((_tot) >> _BITMAP_POWER) + 1)
/* size (in bytes) used to hold '_tot' bits */
#define BLI_BITMAP_SIZE(_tot) \
- (BLI_BITMAP_NUM_BLOCKS(_tot) * sizeof(unsigned int))
+ (_BITMAP_NUM_BLOCKS(_tot) * sizeof(BLI_bitmap))
/* allocate memory for a bitmap with '_tot' bits; free
* with MEM_freeN() */
@@ -60,34 +60,42 @@ typedef unsigned int BLI_bitmap;
((BLI_bitmap *)memset(alloca(BLI_BITMAP_SIZE(_tot)), 0, BLI_BITMAP_SIZE(_tot)))
/* get the value of a single bit at '_index' */
-#define BLI_BITMAP_GET(_bitmap, _index) \
- ((_bitmap)[(_index) >> BLI_BITMAP_POWER] & \
- (1u << ((_index) & BLI_BITMAP_MASK)))
+#define BLI_BITMAP_TEST(_bitmap, _index) \
+ (CHECK_TYPE_INLINE(_bitmap, BLI_bitmap *), \
+ ((_bitmap)[(_index) >> _BITMAP_POWER] & \
+ (1u << ((_index) & _BITMAP_MASK))))
-#define BLI_BITMAP_GET_BOOL(_bitmap, _index) \
- (BLI_BITMAP_GET(_bitmap, _index) != 0)
+#define BLI_BITMAP_TEST_BOOL(_bitmap, _index) \
+ (CHECK_TYPE_INLINE(_bitmap, BLI_bitmap *), \
+ (BLI_BITMAP_TEST(_bitmap, _index) != 0))
/* set the value of a single bit at '_index' */
-#define BLI_BITMAP_SET(_bitmap, _index) \
- ((_bitmap)[(_index) >> BLI_BITMAP_POWER] |= \
- (1u << ((_index) & BLI_BITMAP_MASK)))
+#define BLI_BITMAP_ENABLE(_bitmap, _index) \
+ (CHECK_TYPE_INLINE(_bitmap, BLI_bitmap *), \
+ ((_bitmap)[(_index) >> _BITMAP_POWER] |= \
+ (1u << ((_index) & _BITMAP_MASK))))
/* clear the value of a single bit at '_index' */
-#define BLI_BITMAP_CLEAR(_bitmap, _index) \
- ((_bitmap)[(_index) >> BLI_BITMAP_POWER] &= \
- ~(1u << ((_index) & BLI_BITMAP_MASK)))
+#define BLI_BITMAP_DISABLE(_bitmap, _index) \
+ (CHECK_TYPE_INLINE(_bitmap, BLI_bitmap *), \
+ ((_bitmap)[(_index) >> _BITMAP_POWER] &= \
+ ~(1u << ((_index) & _BITMAP_MASK))))
/* set or clear the value of a single bit at '_index' */
-#define BLI_BITMAP_MODIFY(_bitmap, _index, _set) \
- do { \
+#define BLI_BITMAP_SET(_bitmap, _index, _set) \
+ { \
+ CHECK_TYPE(_bitmap, BLI_bitmap *); \
if (_set) \
- BLI_BITMAP_SET(_bitmap, _index); \
+ BLI_BITMAP_ENABLE(_bitmap, _index); \
else \
- BLI_BITMAP_CLEAR(_bitmap, _index); \
- } while (0)
+ BLI_BITMAP_DISABLE(_bitmap, _index); \
+ } (void)0
/* resize bitmap to have space for '_tot' bits */
#define BLI_BITMAP_RESIZE(_bitmap, _tot) \
- (_bitmap) = MEM_reallocN(_bitmap, BLI_BITMAP_SIZE(_tot))
+ { \
+ CHECK_TYPE(_bitmap, BLI_bitmap *); \
+ (_bitmap) = MEM_reallocN(_bitmap, BLI_BITMAP_SIZE(_tot)); \
+ } (void)0
#endif
diff --git a/source/blender/blenlib/BLI_dynstr.h b/source/blender/blenlib/BLI_dynstr.h
index 61bdf23cec1..484cddd0039 100644
--- a/source/blender/blenlib/BLI_dynstr.h
+++ b/source/blender/blenlib/BLI_dynstr.h
@@ -47,73 +47,17 @@ struct DynStr;
/** The abstract DynStr type */
typedef struct DynStr DynStr;
-/**
- * Create a new DynStr.
- *
- * \return Pointer to a new DynStr.
- */
-DynStr *BLI_dynstr_new(void);
+DynStr *BLI_dynstr_new(void) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
+void BLI_dynstr_append(DynStr *__restrict ds, const char *cstr) ATTR_NONNULL();
+void BLI_dynstr_nappend(DynStr *__restrict ds, const char *cstr, int len) ATTR_NONNULL();
-/**
- * Append a c-string to a DynStr.
- *
- * \param ds The DynStr to append to.
- * \param cstr The c-string to append.
- */
-void BLI_dynstr_append(DynStr *ds, const char *cstr);
+void BLI_dynstr_appendf(DynStr *__restrict ds, const char *__restrict format, ...) ATTR_PRINTF_FORMAT(2, 3) ATTR_NONNULL(1, 2);
+void BLI_dynstr_vappendf(DynStr *__restrict ds, const char *__restrict format, va_list args) ATTR_PRINTF_FORMAT(2, 0) ATTR_NONNULL(1, 2);
-/**
- * Append a length clamped c-string to a DynStr.
- *
- * \param ds The DynStr to append to.
- * \param cstr The c-string to append.
- * \param len The maximum length of the c-string to copy.
- */
-void BLI_dynstr_nappend(DynStr *ds, const char *cstr, int len);
-
-/**
- * Append a c-string to a DynStr, but with formatting like printf.
- *
- * \param ds The DynStr to append to.
- * \param format The printf format string to use.
- */
-void BLI_dynstr_appendf(DynStr *ds, const char *format, ...) ATTR_PRINTF_FORMAT(2, 3);
-void BLI_dynstr_vappendf(DynStr *ds, const char *format, va_list args) ATTR_PRINTF_FORMAT(2, 0);
-
-/**
- * Find the length of a DynStr.
- *
- * \param ds The DynStr of interest.
- * \return The length of \a ds.
- */
-int BLI_dynstr_get_len(DynStr *ds);
-
-/**
- * Get a DynStr's contents as a c-string.
- * <i> The returned c-string should be freed
- * using MEM_freeN. </i>
- *
- * \param ds The DynStr of interest.
- * \return The contents of \a ds as a c-string.
- */
-char *BLI_dynstr_get_cstring(DynStr *ds);
-
-/**
- * Get a DynStr's contents as a c-string.
- * <i> The str argument must be allocated to be at
- * least the size of BLI_dynstr_get_len(ds) + 1. </i>
- *
- * \param ds The DynStr of interest.
- * \param str The string to fill.
- */
-void BLI_dynstr_get_cstring_ex(DynStr *ds, char *str);
-
-/**
- * Free the DynStr
- *
- * \param ds The DynStr to free.
- */
-void BLI_dynstr_free(DynStr *ds);
+int BLI_dynstr_get_len(DynStr *ds) ATTR_NONNULL();
+char *BLI_dynstr_get_cstring(DynStr *ds) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-#endif
+void BLI_dynstr_get_cstring_ex(DynStr *__restrict ds, char *__restrict str) ATTR_NONNULL();
+void BLI_dynstr_free(DynStr *ds) ATTR_NONNULL();
+#endif /* __BLI_DYNSTR_H__ */
diff --git a/source/blender/blenlib/BLI_edgehash.h b/source/blender/blenlib/BLI_edgehash.h
index 8e74ce3a9e3..c0529d9032d 100644
--- a/source/blender/blenlib/BLI_edgehash.h
+++ b/source/blender/blenlib/BLI_edgehash.h
@@ -102,7 +102,7 @@ EdgeSet *BLI_edgeset_new_ex(const char *info,
const unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
EdgeSet *BLI_edgeset_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
int BLI_edgeset_size(EdgeSet *es) ATTR_WARN_UNUSED_RESULT;
-bool BLI_edgeset_reinsert(EdgeSet *es, unsigned int v0, unsigned int v1);
+bool BLI_edgeset_add(EdgeSet *es, unsigned int v0, unsigned int v1);
void BLI_edgeset_insert(EdgeSet *es, unsigned int v0, unsigned int v1);
bool BLI_edgeset_haskey(EdgeSet *eh, unsigned int v0, unsigned int v1) ATTR_WARN_UNUSED_RESULT;
void BLI_edgeset_free(EdgeSet *es);
diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h
index c032b606900..2d1e1d84882 100644
--- a/source/blender/blenlib/BLI_fileops.h
+++ b/source/blender/blenlib/BLI_fileops.h
@@ -60,17 +60,23 @@ int BLI_delete(const char *path, bool dir, bool recursive);
int BLI_move(const char *path, const char *to);
int BLI_create_symlink(const char *path, const char *to);
+/* keep in sync with the definition of struct direntry in BLI_fileops_types.h */
#ifdef WIN32
-# ifndef __MINGW64__
+# if (defined(_MSC_VER) && (_MSC_VER >= 1500)) || defined(__MINGW64__)
typedef struct _stat64 BLI_stat_t;
+# elif defined(__MINGW32__)
+typedef struct _stati64 BLI_stat_t;
# else
-typedef struct stat BLI_stat_t;
-#endif
+typedef struct _stat BLI_stat_t;
+# endif
#else
typedef struct stat BLI_stat_t;
#endif
int BLI_stat(const char *path, BLI_stat_t *buffer);
+#ifdef WIN32
+int BLI_wstat(const wchar_t *path, BLI_stat_t *buffer);
+#endif
/* Directories */
diff --git a/source/blender/blenlib/BLI_fileops_types.h b/source/blender/blenlib/BLI_fileops_types.h
index a7372c01e52..53c9fa16b70 100644
--- a/source/blender/blenlib/BLI_fileops_types.h
+++ b/source/blender/blenlib/BLI_fileops_types.h
@@ -45,12 +45,16 @@ struct direntry {
mode_t type;
char *relname;
char *path;
-#if (defined(WIN32) || defined(WIN64)) && !defined(__MINGW32__) && (_MSC_VER >= 1500)
+#ifdef WIN32 /* keep in sync with the definition of BLI_stat_t in BLI_fileops.h */
+# if (defined(_MSC_VER) && (_MSC_VER >= 1500)) || defined(__MINGW64__)
struct _stat64 s;
-#elif defined(__MINGW32__)
+# elif defined(__MINGW32__)
struct _stati64 s;
+# else
+ struct _stat s;
+# endif
#else
- struct stat s;
+ struct stat s;
#endif
unsigned int flags;
char size[16];
diff --git a/source/blender/blenlib/BLI_ghash.h b/source/blender/blenlib/BLI_ghash.h
index 2aa79d6da8d..a59023d4f9b 100644
--- a/source/blender/blenlib/BLI_ghash.h
+++ b/source/blender/blenlib/BLI_ghash.h
@@ -130,7 +130,7 @@ unsigned int BLI_ghashutil_strhash_p(const void *key);
int BLI_ghashutil_strcmp(const void *a, const void *b);
#define BLI_ghashutil_inthash(key) ( \
- CHECK_TYPE_INLINE(key, int), \
+ CHECK_TYPE_INLINE(&(key), int *), \
BLI_ghashutil_uinthash((unsigned int)key))
unsigned int BLI_ghashutil_uinthash(unsigned int key);
#define BLI_ghashutil_inthash_v4(key) ( \
@@ -191,6 +191,7 @@ GSet *BLI_gset_new(GSetHashFP hashfp, GSetCmpFP cmpfp, const char *info) ATTR_M
int BLI_gset_size(GSet *gs) ATTR_WARN_UNUSED_RESULT;
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp);
void BLI_gset_insert(GSet *gh, void *key);
+bool BLI_gset_add(GSet *gs, void *key);
bool BLI_gset_reinsert(GSet *gh, void *key, GSetKeyFreeFP keyfreefp);
bool BLI_gset_haskey(GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT;
bool BLI_gset_remove(GSet *gs, void *key, GSetKeyFreeFP keyfreefp);
diff --git a/source/blender/blenlib/BLI_gsqueue.h b/source/blender/blenlib/BLI_gsqueue.h
index e002545d189..d17897df665 100644
--- a/source/blender/blenlib/BLI_gsqueue.h
+++ b/source/blender/blenlib/BLI_gsqueue.h
@@ -36,13 +36,13 @@
typedef struct _GSQueue GSQueue;
-GSQueue *BLI_gsqueue_new(int elem_size);
+GSQueue *BLI_gsqueue_new(size_t elem_size);
bool BLI_gsqueue_is_empty(GSQueue *gq);
int BLI_gsqueue_size(GSQueue *gq);
-void BLI_gsqueue_peek(GSQueue *gq, void *item_r);
-void BLI_gsqueue_pop(GSQueue *gq, void *item_r);
-void BLI_gsqueue_push(GSQueue *gq, void *item);
-void BLI_gsqueue_pushback(GSQueue *gq, void *item);
+void BLI_gsqueue_peek(GSQueue *gq, void *r_item);
+void BLI_gsqueue_pop(GSQueue *gq, void *r_item);
+void BLI_gsqueue_push(GSQueue *gq, const void *item);
+void BLI_gsqueue_pushback(GSQueue *gq, const void *item);
void BLI_gsqueue_free(GSQueue *gq);
#endif /* __BLI_GSQUEUE_H__ */
diff --git a/source/blender/blenlib/BLI_linklist_stack.h b/source/blender/blenlib/BLI_linklist_stack.h
index 5f4f98c5a4f..d3d1924628c 100644
--- a/source/blender/blenlib/BLI_linklist_stack.h
+++ b/source/blender/blenlib/BLI_linklist_stack.h
@@ -69,14 +69,14 @@
BLI_linklist_prepend_pool(&(var), ptr, _##var##_pool))
#define BLI_LINKSTACK_POP(var) \
(var ? (typeof(_##var##_type))BLI_linklist_pop_pool(&(var), _##var##_pool) : NULL)
-#define BLI_LINKSTACK_POP_ELSE(var, r) \
+#define BLI_LINKSTACK_POP_DEFAULT(var, r) \
(var ? (typeof(_##var##_type))BLI_linklist_pop_pool(&(var), _##var##_pool) : r)
#else /* non gcc */
#define BLI_LINKSTACK_PUSH(var, ptr) ( \
BLI_linklist_prepend_pool(&(var), ptr, _##var##_pool))
#define BLI_LINKSTACK_POP(var) \
(var ? BLI_linklist_pop_pool(&(var), _##var##_pool) : NULL)
-#define BLI_LINKSTACK_POP_ELSE(var, r) \
+#define BLI_LINKSTACK_POP_DEFAULT(var, r) \
(var ? BLI_linklist_pop_pool(&(var), _##var##_pool) : r)
#endif /* gcc check */
diff --git a/source/blender/blenlib/BLI_listbase.h b/source/blender/blenlib/BLI_listbase.h
index 028892e8f66..b900b5f21a5 100644
--- a/source/blender/blenlib/BLI_listbase.h
+++ b/source/blender/blenlib/BLI_listbase.h
@@ -75,7 +75,7 @@ void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1);
void BLI_movelisttolist(struct ListBase *dst, struct ListBase *src) ATTR_NONNULL(1, 2);
void BLI_duplicatelist(struct ListBase *dst, const struct ListBase *src) ATTR_NONNULL(1, 2);
-void BLI_reverselist(struct ListBase *lb) ATTR_NONNULL(1);
+void BLI_listbase_reverse(struct ListBase *lb) ATTR_NONNULL(1);
void BLI_rotatelist_first(struct ListBase *lb, void *vlink) ATTR_NONNULL(1, 2);
void BLI_rotatelist_last(struct ListBase *lb, void *vlink) ATTR_NONNULL(1, 2);
diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h
index e99518a78c6..5550a1c9fd9 100644
--- a/source/blender/blenlib/BLI_math_matrix.h
+++ b/source/blender/blenlib/BLI_math_matrix.h
@@ -36,19 +36,6 @@ extern "C" {
/********************************* Init **************************************/
-#define MAT4_UNITY { \
- { 1.0, 0.0, 0.0, 0.0}, \
- { 0.0, 1.0, 0.0, 0.0}, \
- { 0.0, 0.0, 1.0, 0.0}, \
- { 0.0, 0.0, 0.0, 1.0} \
-}
-
-#define MAT3_UNITY { \
- { 1.0, 0.0, 0.0}, \
- { 0.0, 1.0, 0.0}, \
- { 0.0, 0.0, 1.0} \
-}
-
void zero_m3(float R[3][3]);
void zero_m4(float R[4][4]);
diff --git a/source/blender/blenlib/BLI_md5.h b/source/blender/blenlib/BLI_md5.h
index 8ce479b1801..6a760f53e45 100644
--- a/source/blender/blenlib/BLI_md5.h
+++ b/source/blender/blenlib/BLI_md5.h
@@ -41,5 +41,7 @@ void *md5_buffer(const char *buffer, size_t len, void *resblock);
int md5_stream(FILE *stream, void *resblock);
+char *md5_to_hexdigest(void *resblock, char r_hex_digest[33]);
+
#endif
diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h
index b33b26a2feb..244c308a05c 100644
--- a/source/blender/blenlib/BLI_path_util.h
+++ b/source/blender/blenlib/BLI_path_util.h
@@ -191,12 +191,14 @@ void BLI_char_switch(char *string, char from, char to) ATTR_NONNULL();
void BLI_init_program_path(const char *argv0);
/* Initialize path to temporary directory.
* NOTE: On Window userdir will be set to the temporary directory! */
-void BLI_init_temporary_dir(char *userdir);
+void BLI_temp_dir_init(char *userdir);
const char *BLI_program_path(void);
const char *BLI_program_dir(void);
-const char *BLI_temporary_dir(void);
+const char *BLI_temp_dir_session(void);
+const char *BLI_temp_dir_base(void);
void BLI_system_temporary_dir(char *dir);
+void BLI_temp_dir_session_purge(void);
#ifdef WITH_ICONV
void BLI_string_to_utf8(char *original, char *utf_8, const char *code);
diff --git a/source/blender/blenlib/BLI_polyfill2d.h b/source/blender/blenlib/BLI_polyfill2d.h
index d434e1b82b9..5c5cea8f67d 100644
--- a/source/blender/blenlib/BLI_polyfill2d.h
+++ b/source/blender/blenlib/BLI_polyfill2d.h
@@ -23,17 +23,10 @@
struct MemArena;
-void BLI_polyfill_calc_ex(
- const float (*coords)[2],
- const unsigned int count,
- unsigned int (*r_tris)[3],
-
- /* avoid allocating each time */
- unsigned int *r_indices, signed char *r_coords_sign);
-
void BLI_polyfill_calc_arena(
const float (*coords)[2],
const unsigned int coords_tot,
+ const int coords_sign,
unsigned int (*r_tris)[3],
struct MemArena *arena);
@@ -41,6 +34,7 @@ void BLI_polyfill_calc_arena(
void BLI_polyfill_calc(
const float (*coords)[2],
const unsigned int coords_tot,
+ const int coords_sign,
unsigned int (*r_tris)[3]);
#endif /* __BLI_POLYFILL2D_H__ */
diff --git a/source/blender/blenlib/BLI_rand.h b/source/blender/blenlib/BLI_rand.h
index 045cadbcc6f..50ab50e449a 100644
--- a/source/blender/blenlib/BLI_rand.h
+++ b/source/blender/blenlib/BLI_rand.h
@@ -50,6 +50,7 @@ int BLI_rng_get_int(struct RNG *rng);
unsigned int BLI_rng_get_uint(struct RNG *rng);
double BLI_rng_get_double(struct RNG *rng);
float BLI_rng_get_float(struct RNG *rng);
+void BLI_rng_get_float_unit_v2(struct RNG *rng, float v[2]);
void BLI_rng_get_float_unit_v3(struct RNG *rng, float v[3]);
void BLI_rng_shuffle_array(struct RNG *rng, void *data, unsigned int elem_size_i, unsigned int elem_tot);
diff --git a/source/blender/blenlib/BLI_sort.h b/source/blender/blenlib/BLI_sort.h
index 4df17d98a4b..516f917a351 100644
--- a/source/blender/blenlib/BLI_sort.h
+++ b/source/blender/blenlib/BLI_sort.h
@@ -33,10 +33,16 @@
* \ingroup bli
*/
+#include <stdlib.h>
+
+#ifdef __GLIBC__
+# define BLI_qsort_r qsort_r
+#endif
+
/* Quick sort reentrant */
-typedef int (*BLI_sort_cmp_t)(void *ctx, const void *a, const void *b);
+typedef int (*BLI_sort_cmp_t)(const void *a, const void *b, void *ctx);
-void BLI_qsort_r(void *a, size_t n, size_t es, void *thunk, BLI_sort_cmp_t cmp)
+void BLI_qsort_r(void *a, size_t n, size_t es, BLI_sort_cmp_t cmp, void *thunk)
#ifdef __GNUC__
__attribute__((nonnull(1, 5)))
#endif
diff --git a/source/blender/blenlib/BLI_stackdefines.h b/source/blender/blenlib/BLI_stackdefines.h
new file mode 100644
index 00000000000..3a4957599d6
--- /dev/null
+++ b/source/blender/blenlib/BLI_stackdefines.h
@@ -0,0 +1,55 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BLI_STACKDEFINES_H__
+#define __BLI_STACKDEFINES_H__
+
+/** \file BLI_stackdefines.h
+ * \ingroup bli
+ */
+
+/* simple stack */
+#define STACK_DECLARE(stack) unsigned int _##stack##_index
+#define STACK_INIT(stack) ((void)stack, (void)((_##stack##_index) = 0))
+#define STACK_SIZE(stack) ((void)stack, (_##stack##_index))
+#define STACK_PUSH(stack, val) (void)((stack)[(_##stack##_index)++] = val)
+#define STACK_PUSH_RET(stack) ((void)stack, ((stack)[(_##stack##_index)++]))
+#define STACK_PUSH_RET_PTR(stack) ((void)stack, &((stack)[(_##stack##_index)++]))
+#define STACK_POP(stack) ((_##stack##_index) ? ((stack)[--(_##stack##_index)]) : NULL)
+#define STACK_POP_PTR(stack) ((_##stack##_index) ? &((stack)[--(_##stack##_index)]) : NULL)
+#define STACK_POP_DEFAULT(stack, r) ((_##stack##_index) ? ((stack)[--(_##stack##_index)]) : r)
+/* take care, re-orders */
+#define STACK_REMOVE(stack, i) \
+ if (--_##stack##_index != i) { \
+ stack[i] = stack[_##stack##_index]; \
+ } (void)0
+#ifdef __GNUC__
+#define STACK_SWAP(stack_a, stack_b) { \
+ SWAP(typeof(stack_a), stack_a, stack_b); \
+ SWAP(unsigned int, _##stack_a##_index, _##stack_b##_index); \
+ } (void)0
+#else
+#define STACK_SWAP(stack_a, stack_b) { \
+ SWAP(void *, stack_a, stack_b); \
+ SWAP(unsigned int, _##stack_a##_index, _##stack_b##_index); \
+ } (void)0
+#endif
+
+#endif /* __BLI_STACKDEFINES_H__ */
diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h
index 2cf904789a2..235147d1bac 100644
--- a/source/blender/blenlib/BLI_string.h
+++ b/source/blender/blenlib/BLI_string.h
@@ -76,6 +76,9 @@ void BLI_ascii_strtolower(char *str, const size_t len) ATTR_NONNULL();
void BLI_ascii_strtoupper(char *str, const size_t len) ATTR_NONNULL();
int BLI_str_rstrip_float_zero(char *str, const char pad) ATTR_NONNULL();
+int BLI_str_index_in_array_n(const char *__restrict str, const char **__restrict str_array, const int str_array_len) ATTR_NONNULL();
+int BLI_str_index_in_array(const char *__restrict str, const char **__restrict str_array) ATTR_NONNULL();
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h
index 1c9e75d950a..74291ca305e 100644
--- a/source/blender/blenlib/BLI_threads.h
+++ b/source/blender/blenlib/BLI_threads.h
@@ -74,8 +74,6 @@ void BLI_end_threaded_malloc(void);
int BLI_system_thread_count(void); /* gets the number of threads the system can make use of */
void BLI_system_num_threads_override_set(int num);
int BLI_system_num_threads_override_get(void);
-
-int BLI_system_thread_count_omp(void);
/* Global Mutex Locks
*
diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h
index f164b55bf36..56931a45495 100644
--- a/source/blender/blenlib/BLI_utildefines.h
+++ b/source/blender/blenlib/BLI_utildefines.h
@@ -147,9 +147,17 @@
__tmp = (__typeof(var_b) *)NULL; \
(void)__tmp; \
} (void)0
+
+#define CHECK_TYPE_PAIR_INLINE(var_a, var_b) ((void)({ \
+ __typeof(var_a) *__tmp; \
+ __tmp = (__typeof(var_b) *)NULL; \
+ (void)__tmp; \
+}))
+
#else
# define CHECK_TYPE(var, type)
# define CHECK_TYPE_PAIR(var_a, var_b)
+# define CHECK_TYPE_PAIR_INLINE(var_a, var_b) (void)0
#endif
/* can be used in simple macros */
@@ -327,11 +335,11 @@
#define IS_EQ(a, b) ( \
CHECK_TYPE_INLINE(a, double), CHECK_TYPE_INLINE(b, double), \
- ((fabs((double)(a) - (b)) >= (double) FLT_EPSILON) ? false : true))
+ ((fabs((double)((a) - (b))) >= (double) FLT_EPSILON) ? false : true))
#define IS_EQF(a, b) ( \
CHECK_TYPE_INLINE(a, float), CHECK_TYPE_INLINE(b, float), \
- ((fabsf((float)(a) - (b)) >= (float) FLT_EPSILON) ? false : true))
+ ((fabsf((float)((a) - (b))) >= (float) FLT_EPSILON) ? false : true))
#define IS_EQT(a, b, c) ((a > b) ? (((a - b) <= c) ? 1 : 0) : ((((b - a) <= c) ? 1 : 0)))
#define IN_RANGE(a, b, c) ((b < c) ? ((b < a && a < c) ? 1 : 0) : ((c < a && a < b) ? 1 : 0))
@@ -346,35 +354,13 @@
#define UNPACK3OP(op, a) op((a)[0]), op((a)[1]), op((a)[2])
#define UNPACK4OP(op, a) op((a)[0]), op((a)[1]), op((a)[2]), op((a)[3])
-/* simple stack */
-#define STACK_DECLARE(stack) unsigned int _##stack##_index
-#define STACK_INIT(stack) ((void)stack, (void)((_##stack##_index) = 0))
-#define STACK_SIZE(stack) ((void)stack, (_##stack##_index))
-#define STACK_PUSH(stack, val) (void)((stack)[(_##stack##_index)++] = val)
-#define STACK_PUSH_RET(stack) ((void)stack, ((stack)[(_##stack##_index)++]))
-#define STACK_PUSH_RET_PTR(stack) ((void)stack, &((stack)[(_##stack##_index)++]))
-#define STACK_POP(stack) ((_##stack##_index) ? ((stack)[--(_##stack##_index)]) : NULL)
-#define STACK_POP_PTR(stack) ((_##stack##_index) ? &((stack)[--(_##stack##_index)]) : NULL)
-#define STACK_POP_ELSE(stack, r) ((_##stack##_index) ? ((stack)[--(_##stack##_index)]) : r)
-#define STACK_FREE(stack) ((void)stack)
-#ifdef __GNUC__
-#define STACK_SWAP(stack_a, stack_b) { \
- SWAP(typeof(stack_a), stack_a, stack_b); \
- SWAP(unsigned int, _##stack_a##_index, _##stack_b##_index); \
- } (void)0
-#else
-#define STACK_SWAP(stack_a, stack_b) { \
- SWAP(void *, stack_a, stack_b); \
- SWAP(unsigned int, _##stack_a##_index, _##stack_b##_index); \
- } (void)0
-#endif
-
/* array helpers */
-#define ARRAY_LAST_ITEM(arr_start, arr_dtype, elem_size, tot) \
- (arr_dtype *)((char *)arr_start + (elem_size * (tot - 1)))
+#define ARRAY_LAST_ITEM(arr_start, arr_dtype, tot) \
+ (arr_dtype *)((char *)arr_start + (sizeof(*((arr_dtype *)NULL)) * (size_t)(tot - 1)))
-#define ARRAY_HAS_ITEM(arr_item, arr_start, tot) \
- ((unsigned int)((arr_item) - (arr_start)) < (unsigned int)(tot))
+#define ARRAY_HAS_ITEM(arr_item, arr_start, tot) ( \
+ CHECK_TYPE_PAIR_INLINE(arr_start, arr_item), \
+ ((unsigned int)((arr_item) - (arr_start)) < (unsigned int)(tot)))
#define ARRAY_DELETE(arr, index, tot_delete, tot) { \
BLI_assert(index + tot_delete <= tot); \
@@ -392,13 +378,28 @@
# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*(arr)))
#endif
+/* Like offsetof(typeof(), member), for non-gcc compilers */
+#define OFFSETOF_STRUCT(_struct, _member) \
+ ((((char *)&((_struct)->_member)) - ((char *)(_struct))) + sizeof((_struct)->_member))
+
+/* memcpy, skipping the first part of a struct,
+ * ensures 'struct_dst' isn't const and that the offset can be computed at compile time */
+#define MEMCPY_STRUCT_OFS(struct_dst, struct_src, member) { \
+ void *_not_const = struct_dst; \
+ (void)_not_const; \
+ ((void)(struct_dst == struct_src), \
+ memcpy((char *)(struct_dst) + OFFSETOF_STRUCT(struct_dst, member), \
+ (char *)(struct_src) + OFFSETOF_STRUCT(struct_dst, member), \
+ sizeof(*(struct_dst)) - OFFSETOF_STRUCT(struct_dst, member))); \
+} (void)0
+
/* Warning-free macros for storing ints in pointers. Use these _only_
* for storing an int in a pointer, not a pointer in an int (64bit)! */
#define SET_INT_IN_POINTER(i) ((void *)(intptr_t)(i))
-#define GET_INT_FROM_POINTER(i) ((int)(intptr_t)(i))
+#define GET_INT_FROM_POINTER(i) ((void)0, ((int)(intptr_t)(i)))
#define SET_UINT_IN_POINTER(i) ((void *)(uintptr_t)(i))
-#define GET_UINT_FROM_POINTER(i) ((unsigned int)(uintptr_t)(i))
+#define GET_UINT_FROM_POINTER(i) ((void)0, ((unsigned int)(uintptr_t)(i)))
/* Macro to convert a value to string in the preprocessor
diff --git a/source/blender/blenlib/BLI_winstuff.h b/source/blender/blenlib/BLI_winstuff.h
index f615e5a9300..f40359ec3dc 100644
--- a/source/blender/blenlib/BLI_winstuff.h
+++ b/source/blender/blenlib/BLI_winstuff.h
@@ -125,10 +125,6 @@ typedef long ssize_t;
# endif
#endif
-
-#ifdef FREE_WINDOWS
-#include <dirent.h>
-#else
struct dirent {
int d_ino;
int d_off;
@@ -151,7 +147,6 @@ typedef struct _DIR {
DIR *opendir(const char *path);
struct dirent *readdir(DIR *dp);
int closedir(DIR *dp);
-#endif
void RegisterBlendExtension(void);
void get_default_root(char *root);
diff --git a/source/blender/blenlib/intern/BLI_dynstr.c b/source/blender/blenlib/intern/BLI_dynstr.c
index 25607d8b499..5c9554203cb 100644
--- a/source/blender/blenlib/intern/BLI_dynstr.c
+++ b/source/blender/blenlib/intern/BLI_dynstr.c
@@ -68,6 +68,11 @@ struct DynStr {
/***/
+/**
+ * Create a new DynStr.
+ *
+ * \return Pointer to a new DynStr.
+ */
DynStr *BLI_dynstr_new(void)
{
DynStr *ds = MEM_mallocN(sizeof(*ds), "DynStr");
@@ -77,6 +82,12 @@ DynStr *BLI_dynstr_new(void)
return ds;
}
+/**
+ * Append a c-string to a DynStr.
+ *
+ * \param ds The DynStr to append to.
+ * \param cstr The c-string to append.
+ */
void BLI_dynstr_append(DynStr *ds, const char *cstr)
{
DynStrElem *dse = malloc(sizeof(*dse));
@@ -94,6 +105,13 @@ void BLI_dynstr_append(DynStr *ds, const char *cstr)
ds->curlen += cstrlen;
}
+/**
+ * Append a length clamped c-string to a DynStr.
+ *
+ * \param ds The DynStr to append to.
+ * \param cstr The c-string to append.
+ * \param len The maximum length of the c-string to copy.
+ */
void BLI_dynstr_nappend(DynStr *ds, const char *cstr, int len)
{
DynStrElem *dse = malloc(sizeof(*dse));
@@ -165,6 +183,12 @@ void BLI_dynstr_vappendf(DynStr *ds, const char *format, va_list args)
}
}
+/**
+ * Append a c-string to a DynStr, but with formatting like printf.
+ *
+ * \param ds The DynStr to append to.
+ * \param format The printf format string to use.
+ */
void BLI_dynstr_appendf(DynStr *ds, const char *format, ...)
{
va_list args;
@@ -221,11 +245,25 @@ void BLI_dynstr_appendf(DynStr *ds, const char *format, ...)
}
}
+/**
+ * Find the length of a DynStr.
+ *
+ * \param ds The DynStr of interest.
+ * \return The length of \a ds.
+ */
int BLI_dynstr_get_len(DynStr *ds)
{
return ds->curlen;
}
+/**
+ * Get a DynStr's contents as a c-string.
+ * <i> The str argument must be allocated to be at
+ * least the size of BLI_dynstr_get_len(ds) + 1. </i>
+ *
+ * \param ds The DynStr of interest.
+ * \param str The string to fill.
+ */
void BLI_dynstr_get_cstring_ex(DynStr *ds, char *rets)
{
char *s;
@@ -242,6 +280,14 @@ void BLI_dynstr_get_cstring_ex(DynStr *ds, char *rets)
rets[ds->curlen] = '\0';
}
+/**
+ * Get a DynStr's contents as a c-string.
+ * <i> The returned c-string should be freed
+ * using MEM_freeN. </i>
+ *
+ * \param ds The DynStr of interest.
+ * \return The contents of \a ds as a c-string.
+ */
char *BLI_dynstr_get_cstring(DynStr *ds)
{
char *rets = MEM_mallocN(ds->curlen + 1, "dynstr_cstring");
@@ -249,6 +295,11 @@ char *BLI_dynstr_get_cstring(DynStr *ds)
return rets;
}
+/**
+ * Free the DynStr
+ *
+ * \param ds The DynStr to free.
+ */
void BLI_dynstr_free(DynStr *ds)
{
DynStrElem *dse;
diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c
index e55f20e99eb..b30553d0b5e 100644
--- a/source/blender/blenlib/intern/BLI_ghash.c
+++ b/source/blender/blenlib/intern/BLI_ghash.c
@@ -901,6 +901,25 @@ void BLI_gset_insert(GSet *gs, void *key)
}
/**
+ * A version of BLI_gset_insert which checks first if the key is in the set.
+ * \returns true if a new key has been added.
+ *
+ * \note GHash has no equivalent to this because typically the value would be different.
+ */
+bool BLI_gset_add(GSet *gs, void *key)
+{
+ const unsigned int hash = ghash_keyhash((GHash *)gs, key);
+ Entry *e = ghash_lookup_entry_ex((GHash *)gs, key, hash);
+ if (e) {
+ return false;
+ }
+ else {
+ ghash_insert_ex_keyonly((GHash *)gs, key, hash);
+ return true;
+ }
+}
+
+/**
* Adds the key to the set (duplicates are managed).
* Matching #BLI_ghash_reinsert
*
diff --git a/source/blender/blenlib/intern/BLI_kdtree.c b/source/blender/blenlib/intern/BLI_kdtree.c
index ed6e6e3ab92..082a54a5774 100644
--- a/source/blender/blenlib/intern/BLI_kdtree.c
+++ b/source/blender/blenlib/intern/BLI_kdtree.c
@@ -207,7 +207,7 @@ int BLI_kdtree_find_nearest(
BLI_assert(tree->is_balanced == true);
#endif
- if (!tree->root)
+ if (UNLIKELY(!tree->root))
return -1;
stack = defaultstack;
@@ -322,7 +322,7 @@ int BLI_kdtree_find_nearest_n__normal(
BLI_assert(tree->is_balanced == true);
#endif
- if (!tree->root || n == 0)
+ if (UNLIKELY(!tree->root || n == 0))
return 0;
stack = defaultstack;
@@ -445,7 +445,7 @@ int BLI_kdtree_range_search__normal(
BLI_assert(tree->is_balanced == true);
#endif
- if (!tree->root)
+ if (UNLIKELY(!tree->root))
return 0;
stack = defaultstack;
diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c
index 448fefa5979..8fc5f97221d 100644
--- a/source/blender/blenlib/intern/BLI_mempool.c
+++ b/source/blender/blenlib/intern/BLI_mempool.c
@@ -409,7 +409,9 @@ void BLI_mempool_free(BLI_mempool *pool, void *addr)
#endif
/* nothing is in use; free all the chunks except the first */
- if (UNLIKELY(pool->totused == 0)) {
+ if (UNLIKELY(pool->totused == 0) &&
+ (pool->chunks->next))
+ {
const unsigned int esize = pool->esize;
BLI_freenode *curnode;
unsigned int j;
diff --git a/source/blender/blenlib/intern/easing.c b/source/blender/blenlib/intern/easing.c
index 1f39c2f57b5..80f02d54eaa 100644
--- a/source/blender/blenlib/intern/easing.c
+++ b/source/blender/blenlib/intern/easing.c
@@ -43,24 +43,18 @@
float BLI_easing_back_ease_in(float time, float begin, float change, float duration, float overshoot)
{
- if (overshoot == 0.0f)
- overshoot = 1.70158f;
time /= duration;
return change * time * time * ((overshoot + 1) * time - overshoot) + begin;
}
float BLI_easing_back_ease_out(float time, float begin, float change, float duration, float overshoot)
{
- if (overshoot == 0.0f)
- overshoot = 1.70158f;
time = time / duration - 1;
return change * (time * time * ((overshoot + 1) * time + overshoot) + 1) + begin;
}
float BLI_easing_back_ease_in_out(float time, float begin, float change, float duration, float overshoot)
{
- if (overshoot == 0.0f)
- overshoot = 1.70158f;
overshoot *= 1.525f;
if ((time /= duration / 2) < 1.0f) {
return change / 2 * (time * time * ((overshoot + 1) * time - overshoot)) + begin;
diff --git a/source/blender/blenlib/intern/edgehash.c b/source/blender/blenlib/intern/edgehash.c
index 51a22cc46ab..173d2a5a590 100644
--- a/source/blender/blenlib/intern/edgehash.c
+++ b/source/blender/blenlib/intern/edgehash.c
@@ -588,9 +588,12 @@ void BLI_edgeset_insert(EdgeSet *es, unsigned int v0, unsigned int v1)
}
/**
- * Assign a new value to a key that may already be in edgehash.
+ * A version of BLI_edgeset_insert which checks first if the key is in the set.
+ * \returns true if a new key has been added.
+ *
+ * \note EdgeHash has no equivalent to this because typically the value would be different.
*/
-bool BLI_edgeset_reinsert(EdgeSet *es, unsigned int v0, unsigned int v1)
+bool BLI_edgeset_add(EdgeSet *es, unsigned int v0, unsigned int v1)
{
unsigned int hash;
EdgeEntry *e;
diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c
index 5df46752e3b..0893c3e380f 100644
--- a/source/blender/blenlib/intern/fileops.c
+++ b/source/blender/blenlib/intern/fileops.c
@@ -48,10 +48,10 @@
# include <io.h>
# include "BLI_winstuff.h"
# include "BLI_callbacks.h"
+# include "BLI_fileops_types.h"
# include "utf_winfunc.h"
# include "utfconv.h"
#else
-# include <unistd.h> // for read close
# include <sys/param.h>
# include <dirent.h>
# include <unistd.h>
@@ -284,26 +284,72 @@ int BLI_access(const char *filename, int mode)
return uaccess(filename, mode);
}
-int BLI_delete(const char *file, bool dir, bool recursive)
+static bool delete_unique(const char *path, const bool dir)
{
- int err;
-
- UTF16_ENCODE(file);
+ bool err;
- if (recursive) {
- callLocalErrorCallBack("Recursive delete is unsupported on Windows");
- err = 1;
- }
- else if (dir) {
- err = !RemoveDirectoryW(file_16);
+ UTF16_ENCODE(path);
+
+ if (dir) {
+ err = !RemoveDirectoryW(path_16);
if (err) printf("Unable to remove directory");
}
else {
- err = !DeleteFileW(file_16);
+ err = !DeleteFileW(path_16);
if (err) callLocalErrorCallBack("Unable to delete file");
}
- UTF16_UN_ENCODE(file);
+ UTF16_UN_ENCODE(path);
+
+ return err;
+}
+
+static bool delete_recursive(const char *dir)
+{
+ struct direntry *filelist, *fl;
+ bool err = false;
+ unsigned int nbr, i;
+
+ i = nbr = BLI_dir_contents(dir, &filelist);
+ fl = filelist;
+ while (i--) {
+ char file[8];
+ BLI_split_file_part(fl->path, file, sizeof(file));
+ if (STREQ(file, ".") || STREQ(file, "..")) {
+ /* Skip! */
+ }
+ else if (S_ISDIR(fl->type)) {
+ if (delete_recursive(fl->path)) {
+ err = true;
+ }
+ }
+ else {
+ if (delete_unique(fl->path, false)) {
+ err = true;
+ }
+ }
+ ++fl;
+ }
+
+ if (!err && delete_unique(dir, true)) {
+ err = true;
+ }
+
+ BLI_free_filelist(filelist, nbr);
+
+ return err;
+}
+
+int BLI_delete(const char *file, bool dir, bool recursive)
+{
+ int err;
+
+ if (recursive) {
+ err = delete_recursive(file);
+ }
+ else {
+ err = delete_unique(file, dir);
+ }
return err;
}
diff --git a/source/blender/blenlib/intern/gsqueue.c b/source/blender/blenlib/intern/gsqueue.c
index 6967742f89b..67703c3f033 100644
--- a/source/blender/blenlib/intern/gsqueue.c
+++ b/source/blender/blenlib/intern/gsqueue.c
@@ -40,12 +40,13 @@
typedef struct _GSQueueElem GSQueueElem;
struct _GSQueueElem {
GSQueueElem *next;
+ char data[0];
};
struct _GSQueue {
GSQueueElem *head;
GSQueueElem *tail;
- int elem_size;
+ size_t elem_size;
};
/**
@@ -54,7 +55,7 @@ struct _GSQueue {
* \param elem_size The size of the structures in the queue.
* \retval The new queue
*/
-GSQueue *BLI_gsqueue_new(int elem_size)
+GSQueue *BLI_gsqueue_new(size_t elem_size)
{
GSQueue *gq = MEM_mallocN(sizeof(*gq), "gqueue_new");
gq->head = gq->tail = NULL;
@@ -92,9 +93,9 @@ int BLI_gsqueue_size(GSQueue *gq)
* \param item_r A pointer to an appropriately
* sized structure (the size passed to BLI_gsqueue_new)
*/
-void BLI_gsqueue_peek(GSQueue *gq, void *item_r)
+void BLI_gsqueue_peek(GSQueue *gq, void *r_item)
{
- memcpy(item_r, &gq->head[1], (size_t)gq->elem_size);
+ memcpy(r_item, &gq->head->data, gq->elem_size);
}
/**
@@ -105,7 +106,7 @@ void BLI_gsqueue_peek(GSQueue *gq, void *item_r)
* sized structure (the size passed to BLI_gsqueue_new).
* Can be NULL if desired.
*/
-void BLI_gsqueue_pop(GSQueue *gq, void *item_r)
+void BLI_gsqueue_pop(GSQueue *gq, void *r_item)
{
GSQueueElem *elem = gq->head;
if (elem == gq->tail) {
@@ -115,7 +116,9 @@ void BLI_gsqueue_pop(GSQueue *gq, void *item_r)
gq->head = gq->head->next;
}
- if (item_r) memcpy(item_r, &elem[1], (size_t)gq->elem_size);
+ if (r_item) {
+ memcpy(r_item, elem->data, gq->elem_size);
+ }
MEM_freeN(elem);
}
@@ -125,17 +128,17 @@ void BLI_gsqueue_pop(GSQueue *gq, void *item_r)
* \param item A pointer to an appropriately
* sized structure (the size passed to BLI_gsqueue_new).
*/
-void BLI_gsqueue_push(GSQueue *gq, void *item)
+void BLI_gsqueue_push(GSQueue *gq, const void *item)
{
GSQueueElem *elem;
/* compare: prevent events added double in row */
if (!BLI_gsqueue_is_empty(gq)) {
- if (0 == memcmp(item, &gq->head[1], (size_t)gq->elem_size))
+ if (0 == memcmp(item, gq->head->data, gq->elem_size))
return;
}
- elem = MEM_mallocN(sizeof(*elem) + (size_t)gq->elem_size, "gqueue_push");
- memcpy(&elem[1], item, (size_t)gq->elem_size);
+ elem = MEM_mallocN(sizeof(*elem) + gq->elem_size, "gqueue_push");
+ memcpy(elem->data, item, gq->elem_size);
elem->next = NULL;
if (BLI_gsqueue_is_empty(gq)) {
@@ -153,10 +156,10 @@ void BLI_gsqueue_push(GSQueue *gq, void *item)
* \param item A pointer to an appropriately
* sized structure (the size passed to BLI_gsqueue_new).
*/
-void BLI_gsqueue_pushback(GSQueue *gq, void *item)
+void BLI_gsqueue_pushback(GSQueue *gq, const void *item)
{
- GSQueueElem *elem = MEM_mallocN(sizeof(*elem) + (size_t)gq->elem_size, "gqueue_push");
- memcpy(&elem[1], item, (size_t)gq->elem_size);
+ GSQueueElem *elem = MEM_mallocN(sizeof(*elem) + gq->elem_size, "gqueue_push");
+ memcpy(elem->data, item, gq->elem_size);
elem->next = gq->head;
if (BLI_gsqueue_is_empty(gq)) {
diff --git a/source/blender/blenlib/intern/listbase.c b/source/blender/blenlib/intern/listbase.c
index 76ad687de3c..b0c24899bd1 100644
--- a/source/blender/blenlib/intern/listbase.c
+++ b/source/blender/blenlib/intern/listbase.c
@@ -577,7 +577,7 @@ void BLI_duplicatelist(ListBase *dst, const ListBase *src)
}
}
-void BLI_reverselist(ListBase *lb)
+void BLI_listbase_reverse(ListBase *lb)
{
struct Link *curr = lb->first;
struct Link *prev = NULL;
diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c
index b971b81fba6..dce2e9d54e3 100644
--- a/source/blender/blenlib/intern/math_rotation.c
+++ b/source/blender/blenlib/intern/math_rotation.c
@@ -1088,25 +1088,24 @@ static void mat3_to_eul2(float tmat[3][3], float eul1[3], float eul2[3])
mat3_to_quat(quat, tmat);
quat_to_mat3(mat, quat);
- copy_m3_m3(mat, tmat);
- normalize_m3(mat);
+ normalize_m3_m3(mat, tmat);
- cy = sqrtf(mat[0][0] * mat[0][0] + mat[0][1] * mat[0][1]);
+ cy = hypotf(mat[0][0], mat[0][1]);
if (cy > 16.0f * FLT_EPSILON) {
- eul1[0] = (float)atan2(mat[1][2], mat[2][2]);
- eul1[1] = (float)atan2(-mat[0][2], cy);
- eul1[2] = (float)atan2(mat[0][1], mat[0][0]);
+ eul1[0] = atan2f(mat[1][2], mat[2][2]);
+ eul1[1] = atan2f(-mat[0][2], cy);
+ eul1[2] = atan2f(mat[0][1], mat[0][0]);
- eul2[0] = (float)atan2(-mat[1][2], -mat[2][2]);
- eul2[1] = (float)atan2(-mat[0][2], -cy);
- eul2[2] = (float)atan2(-mat[0][1], -mat[0][0]);
+ eul2[0] = atan2f(-mat[1][2], -mat[2][2]);
+ eul2[1] = atan2f(-mat[0][2], -cy);
+ eul2[2] = atan2f(-mat[0][1], -mat[0][0]);
}
else {
- eul1[0] = (float)atan2(-mat[2][1], mat[1][1]);
- eul1[1] = (float)atan2(-mat[0][2], cy);
+ eul1[0] = atan2f(-mat[2][1], mat[1][1]);
+ eul1[1] = atan2f(-mat[0][2], cy);
eul1[2] = 0.0f;
copy_v3_v3(eul2, eul1);
@@ -1380,44 +1379,38 @@ void eulO_to_mat3(float M[3][3], const float e[3], const short order)
}
/* returns two euler calculation methods, so we can pick the best */
-static void mat3_to_eulo2(float M[3][3], float e1[3], float e2[3], const short order)
+static void mat3_to_eulo2(float M[3][3], float eul1[3], float eul2[3], const short order)
{
const RotOrderInfo *R = GET_ROTATIONORDER_INFO(order);
short i = R->axis[0], j = R->axis[1], k = R->axis[2];
- float m[3][3];
- double cy;
+ float mat[3][3];
+ float cy;
/* process the matrix first */
- copy_m3_m3(m, M);
- normalize_m3(m);
+ normalize_m3_m3(mat, M);
- cy = sqrt(m[i][i] * m[i][i] + m[i][j] * m[i][j]);
+ cy = hypotf(mat[i][i], mat[i][j]);
- if (cy > 16.0 * (double)FLT_EPSILON) {
- e1[i] = atan2f(m[j][k], m[k][k]);
- e1[j] = atan2f(-m[i][k], (float)cy);
- e1[k] = atan2f(m[i][j], m[i][i]);
+ if (cy > 16.0f * FLT_EPSILON) {
+ eul1[i] = atan2f(mat[j][k], mat[k][k]);
+ eul1[j] = atan2f(-mat[i][k], cy);
+ eul1[k] = atan2f(mat[i][j], mat[i][i]);
- e2[i] = atan2f(-m[j][k], -m[k][k]);
- e2[j] = atan2f(-m[i][k], (float)-cy);
- e2[k] = atan2f(-m[i][j], -m[i][i]);
+ eul2[i] = atan2f(-mat[j][k], -mat[k][k]);
+ eul2[j] = atan2f(-mat[i][k], -cy);
+ eul2[k] = atan2f(-mat[i][j], -mat[i][i]);
}
else {
- e1[i] = atan2f(-m[k][j], m[j][j]);
- e1[j] = atan2f(-m[i][k], (float)cy);
- e1[k] = 0;
+ eul1[i] = atan2f(-mat[k][j], mat[j][j]);
+ eul1[j] = atan2f(-mat[i][k], cy);
+ eul1[k] = 0;
- copy_v3_v3(e2, e1);
+ copy_v3_v3(eul2, eul1);
}
if (R->parity) {
- e1[0] = -e1[0];
- e1[1] = -e1[1];
- e1[2] = -e1[2];
-
- e2[0] = -e2[0];
- e2[1] = -e2[1];
- e2[2] = -e2[2];
+ negate_v3(eul1);
+ negate_v3(eul2);
}
}
diff --git a/source/blender/blenlib/intern/md5.c b/source/blender/blenlib/intern/md5.c
index 4a09afe2e3d..3d1a9cdb7a4 100644
--- a/source/blender/blenlib/intern/md5.c
+++ b/source/blender/blenlib/intern/md5.c
@@ -1,417 +1,408 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ * Copyright (C) 1995 Software Foundation, Inc.
+ *
+ * Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>.
+ */
+
/** \file blender/blenlib/intern/md5.c
- * \ingroup imbuf
+ * \ingroup bli
+ *
+ * Functions to compute MD5 message digest of files or memory blocks
+ * according to the definition of MD5 in RFC 1321 from April 1992.
*/
#include "BLI_md5.h" /* own include */
-/* md5.c - Functions to compute MD5 message digest of files or memory blocks
- according to the definition of MD5 in RFC 1321 from April 1992.
- Copyright (C) 1995 Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-
-/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>. */
-
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#if defined HAVE_LIMITS_H || defined _LIBC
-# include <limits.h>
+# include <limits.h>
#endif
-/* The following contortions are an attempt to use the C preprocessor
- to determine an unsigned integral type that is 32 bits wide. An
- alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
- doing that would require that the configure script compile and *run*
- the resulting executable. Locally running cross-compiled executables
- is usually not possible. */
+/* The following contortions are an attempt to use the C preprocessor to determine an unsigned integral type
+ * that is 32 bits wide. An alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but doing that
+ * would require that the configure script compile and *run* the resulting executable.
+ * Locally running cross-compiled executables is usually not possible.
+ */
#if defined __STDC__ && __STDC__
-# define UINT_MAX_32_BITS 4294967295U
+# define UINT_MAX_32_BITS 4294967295U
#else
-# define UINT_MAX_32_BITS 0xFFFFFFFF
+# define UINT_MAX_32_BITS 0xFFFFFFFF
#endif
/* If UINT_MAX isn't defined, assume it's a 32-bit type.
- This should be valid for all systems GNU cares about because
- that doesn't include 16-bit systems, and only modern systems
- (that certainly have <limits.h>) have 64+-bit integral types. */
+ * This should be valid for all systems GNU cares about because that doesn't include 16-bit systems,
+ * and only modern systems (that certainly have <limits.h>) have 64+-bit integral types.
+ */
#ifndef UINT_MAX
-# define UINT_MAX UINT_MAX_32_BITS
+# define UINT_MAX UINT_MAX_32_BITS
#endif
#if UINT_MAX == UINT_MAX_32_BITS
- typedef unsigned int md5_uint32;
+ typedef unsigned int md5_uint32;
#else
-# if USHRT_MAX == UINT_MAX_32_BITS
- typedef unsigned short md5_uint32;
-# else
-# if ULONG_MAX == UINT_MAX_32_BITS
- typedef unsigned long md5_uint32;
+# if USHRT_MAX == UINT_MAX_32_BITS
+ typedef unsigned short md5_uint32;
# else
- /* The following line is intended to evoke an error.
- Using #error is not portable enough. */
- "Cannot determine unsigned 32-bit data type."
+# if ULONG_MAX == UINT_MAX_32_BITS
+ typedef unsigned long md5_uint32;
+# else
+ /* The following line is intended to evoke an error. Using #error is not portable enough. */
+ "Cannot determine unsigned 32-bit data type."
+# endif
# endif
-# endif
#endif
-/* Structure to save state of computation between the single steps. */
+
+/* Following code is low level, upon which are built up the functions 'md5_stream' and 'md5_buffer'. */
+
+/* Structure to save state of computation between the single steps. */
struct md5_ctx
{
- md5_uint32 A;
- md5_uint32 B;
- md5_uint32 C;
- md5_uint32 D;
+ md5_uint32 A;
+ md5_uint32 B;
+ md5_uint32 C;
+ md5_uint32 D;
};
-/*
- * The following three functions are build up the low level used in
- * the functions `md5_stream' and `md5_buffer'.
- */
+#ifdef __BIG_ENDIAN__
+# define SWAP(n) (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+#else
+# define SWAP(n) (n)
+#endif
-/* Initialize structure containing state of computation.
- (RFC 1321, 3.3: Step 3) */
-static void md5_init_ctx(struct md5_ctx *ctx);
+/* This array contains the bytes used to pad the buffer to the next 64-byte boundary. (RFC 1321, 3.1: Step 1) */
+static const unsigned char fillbuf[64] = {0x80, 0 /* , 0, 0, ... */};
-/* Starting with the result of former calls of this function (or the
- initialzation function update the context for the next LEN bytes
- starting at BUFFER.
- It is necessary that LEN is a multiple of 64!!! */
-static void md5_process_block(const void *buffer, size_t len, struct md5_ctx *ctx);
+/** Initialize structure containing state of computation.
+ * (RFC 1321, 3.3: Step 3)
+ */
+static void md5_init_ctx(struct md5_ctx *ctx)
+{
+ ctx->A = 0x67452301;
+ ctx->B = 0xefcdab89;
+ ctx->C = 0x98badcfe;
+ ctx->D = 0x10325476;
+}
-/* Put result from CTX in first 16 bytes following RESBUF. The result is
- always in little endian byte order, so that a byte-wise output yields
- to the wanted ASCII representation of the message digest. */
-static void *md5_read_ctx(const struct md5_ctx *ctx, void *resbuf);
+/** Starting with the result of former calls of this function (or the initialization), this function updates
+ * the 'ctx' context for the next 'len' bytes starting at 'buffer'.
+ * It is necessary that 'len' is a multiple of 64!!!
+ */
+static void md5_process_block (const void *buffer, size_t len, struct md5_ctx *ctx)
+{
+/* These are the four functions used in the four steps of the MD5 algorithm and defined in the RFC 1321.
+ * The first function is a little bit optimized (as found in Colin Plumbs public domain implementation).
+ */
+/* #define FF(b, c, d) ((b & c) | (~b & d)) */
+#define FF(b, c, d) (d ^ (b & (c ^ d)))
+#define FG(b, c, d) FF (d, b, c)
+#define FH(b, c, d) (b ^ c ^ d)
+#define FI(b, c, d) (c ^ (b | ~d))
+/* It is unfortunate that C does not provide an operator for cyclic rotation. Hope the C compiler is smart enough. */
+#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
-#ifdef __BIG_ENDIAN__
-# define SWAP(n) \
- (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
-#else
-# define SWAP(n) (n)
-#endif
+ md5_uint32 correct_words[16];
+ const md5_uint32 *words = buffer;
+ size_t nwords = len / sizeof(md5_uint32);
+ const md5_uint32 *endp = words + nwords;
+ md5_uint32 A = ctx->A;
+ md5_uint32 B = ctx->B;
+ md5_uint32 C = ctx->C;
+ md5_uint32 D = ctx->D;
+
+ /* Process all bytes in the buffer with 64 bytes in each round of the loop. */
+ while (words < endp) {
+ md5_uint32 *cwp = correct_words;
+ md5_uint32 A_save = A;
+ md5_uint32 B_save = B;
+ md5_uint32 C_save = C;
+ md5_uint32 D_save = D;
+
+ /* First round: using the given function, the context and a constant the next context is computed.
+ * Because the algorithms processing unit is a 32-bit word and it is determined to work on words in
+ * little endian byte order we perhaps have to change the byte order before the computation.
+ * To reduce the work for the next steps we store the swapped words in the array CORRECT_WORDS.
+ */
+#define OP(a, b, c, d, s, T) \
+ a += FF(b, c, d) + (*cwp++ = SWAP(*words)) + T; \
+ ++words; \
+ CYCLIC(a, s); \
+ a += b; \
+ (void)0
+
+ /* Before we start, one word to the strange constants. They are defined in RFC 1321 as:
+ * T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
+ */
+
+ /* Round 1. */
+ OP(A, B, C, D, 7, 0xd76aa478);
+ OP(D, A, B, C, 12, 0xe8c7b756);
+ OP(C, D, A, B, 17, 0x242070db);
+ OP(B, C, D, A, 22, 0xc1bdceee);
+ OP(A, B, C, D, 7, 0xf57c0faf);
+ OP(D, A, B, C, 12, 0x4787c62a);
+ OP(C, D, A, B, 17, 0xa8304613);
+ OP(B, C, D, A, 22, 0xfd469501);
+ OP(A, B, C, D, 7, 0x698098d8);
+ OP(D, A, B, C, 12, 0x8b44f7af);
+ OP(C, D, A, B, 17, 0xffff5bb1);
+ OP(B, C, D, A, 22, 0x895cd7be);
+ OP(A, B, C, D, 7, 0x6b901122);
+ OP(D, A, B, C, 12, 0xfd987193);
+ OP(C, D, A, B, 17, 0xa679438e);
+ OP(B, C, D, A, 22, 0x49b40821);
+#undef OP
-/* This array contains the bytes used to pad the buffer to the next
- 64-byte boundary. (RFC 1321, 3.1: Step 1) */
-static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
+ /* For the second to fourth round we have the possibly swapped words in CORRECT_WORDS.
+ * Redefine the macro to take an additional first argument specifying the function to use.
+ */
+#define OP(f, a, b, c, d, k, s, T) \
+ a += f(b, c, d) + correct_words[k] + T; \
+ CYCLIC(a, s); \
+ a += b; \
+ (void)0
+
+ /* Round 2. */
+ OP(FG, A, B, C, D, 1, 5, 0xf61e2562);
+ OP(FG, D, A, B, C, 6, 9, 0xc040b340);
+ OP(FG, C, D, A, B, 11, 14, 0x265e5a51);
+ OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
+ OP(FG, A, B, C, D, 5, 5, 0xd62f105d);
+ OP(FG, D, A, B, C, 10, 9, 0x02441453);
+ OP(FG, C, D, A, B, 15, 14, 0xd8a1e681);
+ OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
+ OP(FG, A, B, C, D, 9, 5, 0x21e1cde6);
+ OP(FG, D, A, B, C, 14, 9, 0xc33707d6);
+ OP(FG, C, D, A, B, 3, 14, 0xf4d50d87);
+ OP(FG, B, C, D, A, 8, 20, 0x455a14ed);
+ OP(FG, A, B, C, D, 13, 5, 0xa9e3e905);
+ OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8);
+ OP(FG, C, D, A, B, 7, 14, 0x676f02d9);
+ OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+
+ /* Round 3. */
+ OP(FH, A, B, C, D, 5, 4, 0xfffa3942);
+ OP(FH, D, A, B, C, 8, 11, 0x8771f681);
+ OP(FH, C, D, A, B, 11, 16, 0x6d9d6122);
+ OP(FH, B, C, D, A, 14, 23, 0xfde5380c);
+ OP(FH, A, B, C, D, 1, 4, 0xa4beea44);
+ OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9);
+ OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60);
+ OP(FH, B, C, D, A, 10, 23, 0xbebfbc70);
+ OP(FH, A, B, C, D, 13, 4, 0x289b7ec6);
+ OP(FH, D, A, B, C, 0, 11, 0xeaa127fa);
+ OP(FH, C, D, A, B, 3, 16, 0xd4ef3085);
+ OP(FH, B, C, D, A, 6, 23, 0x04881d05);
+ OP(FH, A, B, C, D, 9, 4, 0xd9d4d039);
+ OP(FH, D, A, B, C, 12, 11, 0xe6db99e5);
+ OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+ OP(FH, B, C, D, A, 2, 23, 0xc4ac5665);
+
+ /* Round 4. */
+ OP(FI, A, B, C, D, 0, 6, 0xf4292244);
+ OP(FI, D, A, B, C, 7, 10, 0x432aff97);
+ OP(FI, C, D, A, B, 14, 15, 0xab9423a7);
+ OP(FI, B, C, D, A, 5, 21, 0xfc93a039);
+ OP(FI, A, B, C, D, 12, 6, 0x655b59c3);
+ OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92);
+ OP(FI, C, D, A, B, 10, 15, 0xffeff47d);
+ OP(FI, B, C, D, A, 1, 21, 0x85845dd1);
+ OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f);
+ OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+ OP(FI, C, D, A, B, 6, 15, 0xa3014314);
+ OP(FI, B, C, D, A, 13, 21, 0x4e0811a1);
+ OP(FI, A, B, C, D, 4, 6, 0xf7537e82);
+ OP(FI, D, A, B, C, 11, 10, 0xbd3af235);
+ OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
+ OP(FI, B, C, D, A, 9, 21, 0xeb86d391);
+#undef OP
-/* Initialize structure containing state of computation.
- (RFC 1321, 3.3: Step 3) */
-static void md5_init_ctx(struct md5_ctx *ctx)
-{
- ctx->A = 0x67452301;
- ctx->B = 0xefcdab89;
- ctx->C = 0x98badcfe;
- ctx->D = 0x10325476;
+ /* Add the starting values of the context. */
+ A += A_save;
+ B += B_save;
+ C += C_save;
+ D += D_save;
+ }
+
+ /* Put checksum in context given as argument. */
+ ctx->A = A;
+ ctx->B = B;
+ ctx->C = C;
+ ctx->D = D;
+
+#undef FF
+#undef FG
+#undef FH
+#undef FI
+#undef CYCLIC
}
-/* Put result from CTX in first 16 bytes following RESBUF. The result must
- be in little endian byte order. */
+/** Put result from 'ctx' in first 16 bytes of 'resbuf'. The result is always in little endian byte order,
+ * so that a byte-wise output yields to the wanted ASCII representation of the message digest.
+ */
static void *md5_read_ctx(const struct md5_ctx *ctx, void *resbuf)
{
- ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A);
- ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B);
- ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C);
- ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D);
+ md5_uint32 *digest = resbuf;
+ digest[0] = SWAP(ctx->A);
+ digest[1] = SWAP(ctx->B);
+ digest[2] = SWAP(ctx->C);
+ digest[3] = SWAP(ctx->D);
- return resbuf;
+ return resbuf;
}
-/* Compute MD5 message digest for bytes read from STREAM. The
- resulting message digest number will be written into the 16 bytes
- beginning at RESBLOCK. */
+/* Top level public functions. */
+
+/** Compute MD5 message digest for bytes read from 'stream'.
+ * The resulting message digest number will be written into the 16 bytes beginning at 'resblock'.
+ * \return Non-zero if an error occurred.
+ */
int md5_stream(FILE *stream, void *resblock)
{
- /* Important: BLOCKSIZE must be a multiple of 64. */
-#define BLOCKSIZE 4096
- struct md5_ctx ctx;
- md5_uint32 len[2];
- char buffer[BLOCKSIZE + 72];
- size_t pad, sum;
-
- /* Initialize the computation context. */
- md5_init_ctx (&ctx);
-
- len[0] = 0;
- len[1] = 0;
-
- /* Iterate over full file contents. */
- while (1)
- {
- /* We read the file in blocks of BLOCKSIZE bytes. One call of the
- computation function processes the whole buffer so that with the
- next round of the loop another block can be read. */
- size_t n;
- sum = 0;
-
- /* Read block. Take care for partial reads. */
- do
- {
- n = fread (buffer, 1, BLOCKSIZE - sum, stream);
-
- sum += n;
- }
- while (sum < BLOCKSIZE && n != 0);
- if (n == 0 && ferror (stream))
- return 1;
-
- /* RFC 1321 specifies the possible length of the file up to 2^64 bits.
- Here we only compute the number of bytes. Do a double word
- increment. */
- len[0] += sum;
- if (len[0] < sum)
- ++len[1];
-
- /* If end of file is reached, end the loop. */
- if (n == 0)
- break;
-
- /* Process buffer with BLOCKSIZE bytes. Note that
- BLOCKSIZE % 64 == 0
- */
- md5_process_block (buffer, BLOCKSIZE, &ctx);
+#define BLOCKSIZE 4096 /* Important: must be a multiple of 64. */
+ struct md5_ctx ctx;
+ md5_uint32 len[2];
+ char buffer[BLOCKSIZE + 72];
+ size_t pad, sum;
+
+ /* Initialize the computation context. */
+ md5_init_ctx(&ctx);
+
+ len[0] = 0;
+ len[1] = 0;
+
+ /* Iterate over full file contents. */
+ while (1) {
+ /* We read the file in blocks of BLOCKSIZE bytes. One call of the computation function processes
+ * the whole buffer so that with the next round of the loop another block can be read.
+ */
+ size_t n;
+ sum = 0;
+
+ /* Read block. Take care for partial reads. */
+ do {
+ n = fread(buffer, 1, BLOCKSIZE - sum, stream);
+ sum += n;
+ } while (sum < BLOCKSIZE && n != 0);
+
+ if (n == 0 && ferror(stream))
+ return 1;
+
+ /* RFC 1321 specifies the possible length of the file up to 2^64 bits.
+ * Here we only compute the number of bytes. Do a double word increment.
+ */
+ len[0] += sum;
+ if (len[0] < sum)
+ ++len[1];
+
+ /* If end of file is reached, end the loop. */
+ if (n == 0)
+ break;
+
+ /* Process buffer with BLOCKSIZE bytes. Note that BLOCKSIZE % 64 == 0. */
+ md5_process_block(buffer, BLOCKSIZE, &ctx);
}
- /* We can copy 64 byte because the buffer is always big enough. FILLBUF
- contains the needed bits. */
- memcpy (&buffer[sum], fillbuf, 64);
+ /* We can copy 64 bytes because the buffer is always big enough. 'fillbuf' contains the needed bits. */
+ memcpy(&buffer[sum], fillbuf, 64);
- /* Compute amount of padding bytes needed. Alignment is done to
- (N + PAD) % 64 == 56
- There is always at least one byte padded. I.e. even the alignment
- is correctly aligned 64 padding bytes are added. */
- pad = sum & 63;
- pad = pad >= 56 ? 64 + 56 - pad : 56 - pad;
+ /* Compute amount of padding bytes needed. Alignment is done to (N + PAD) % 64 == 56.
+ * There is always at least one byte padded, i.e. if the alignment is correctly aligned,
+ * 64 padding bytes are added.
+ */
+ pad = sum & 63;
+ pad = pad >= 56 ? 64 + 56 - pad : 56 - pad;
- /* Put the 64-bit file length in *bits* at the end of the buffer. */
- *(md5_uint32 *) &buffer[sum + pad] = SWAP (len[0] << 3);
- *(md5_uint32 *) &buffer[sum + pad + 4] = SWAP ((len[1] << 3)
- | (len[0] >> 29));
+ /* Put the 64-bit file length in *bits* at the end of the buffer. */
+ *(md5_uint32 *) &buffer[sum + pad] = SWAP(len[0] << 3);
+ *(md5_uint32 *) &buffer[sum + pad + 4] = SWAP((len[1] << 3) | (len[0] >> 29));
- /* Process last bytes. */
- md5_process_block (buffer, sum + pad + 8, &ctx);
+ /* Process last bytes. */
+ md5_process_block(buffer, sum + pad + 8, &ctx);
- /* Construct result in desired memory. */
- md5_read_ctx (&ctx, resblock);
- return 0;
+ /* Construct result in desired memory. */
+ md5_read_ctx(&ctx, resblock);
+ return 0;
}
-/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
- result is always in little endian byte order, so that a byte-wise
- output yields to the wanted ASCII representation of the message
- digest. */
+/** Compute MD5 message digest for 'len' bytes beginning at 'buffer'.
+ * The result is always in little endian byte order, so that a byte-wise output yields to the wanted
+ * ASCII representation of the message digest.
+ */
void *md5_buffer(const char *buffer, size_t len, void *resblock)
{
- struct md5_ctx ctx;
- char restbuf[64 + 72];
- size_t blocks = len & ~63;
- size_t pad, rest;
-
- /* Initialize the computation context. */
- md5_init_ctx (&ctx);
-
- /* Process whole buffer but last len % 64 bytes. */
- md5_process_block (buffer, blocks, &ctx);
-
- /* REST bytes are not processed yet. */
- rest = len - blocks;
- /* Copy to own buffer. */
- memcpy (restbuf, &buffer[blocks], rest);
- /* Append needed fill bytes at end of buffer. We can copy 64 byte
- because the buffer is always big enough. */
- memcpy (&restbuf[rest], fillbuf, 64);
-
- /* PAD bytes are used for padding to correct alignment. Note that
- always at least one byte is padded. */
- pad = rest >= 56 ? 64 + 56 - rest : 56 - rest;
-
- /* Put length of buffer in *bits* in last eight bytes. */
- *(md5_uint32 *) &restbuf[rest + pad] = (md5_uint32) SWAP (len << 3);
- *(md5_uint32 *) &restbuf[rest + pad + 4] = (md5_uint32) SWAP (len >> 29);
-
- /* Process last bytes. */
- md5_process_block (restbuf, rest + pad + 8, &ctx);
-
- /* Put result in desired memory area. */
- return md5_read_ctx (&ctx, resblock);
-}
+ struct md5_ctx ctx;
+ char restbuf[64 + 72];
+ size_t blocks = len & ~63;
+ size_t pad, rest;
+ /* Initialize the computation context. */
+ md5_init_ctx(&ctx);
-/* These are the four functions used in the four steps of the MD5 algorithm
- and defined in the RFC 1321. The first function is a little bit optimized
- (as found in Colin Plumbs public domain implementation). */
-/* #define FF(b, c, d) ((b & c) | (~b & d)) */
-#define FF(b, c, d) (d ^ (b & (c ^ d)))
-#define FG(b, c, d) FF (d, b, c)
-#define FH(b, c, d) (b ^ c ^ d)
-#define FI(b, c, d) (c ^ (b | ~d))
+ /* Process whole buffer but last len % 64 bytes. */
+ md5_process_block(buffer, blocks, &ctx);
-/* Process LEN bytes of BUFFER, accumulating context into CTX.
- It is assumed that LEN % 64 == 0. */
+ /* REST bytes are not processed yet. */
+ rest = len - blocks;
+ /* Copy to own buffer. */
+ memcpy(restbuf, &buffer[blocks], rest);
+ /* Append needed fill bytes at end of buffer. We can copy 64 bytes because the buffer is always big enough. */
+ memcpy(&restbuf[rest], fillbuf, 64);
-void md5_process_block (const void *buffer, size_t len, struct md5_ctx *ctx)
-{
- md5_uint32 correct_words[16];
- const md5_uint32 *words = buffer;
- size_t nwords = len / sizeof (md5_uint32);
- const md5_uint32 *endp = words + nwords;
- md5_uint32 A = ctx->A;
- md5_uint32 B = ctx->B;
- md5_uint32 C = ctx->C;
- md5_uint32 D = ctx->D;
-
- /* Process all bytes in the buffer with 64 bytes in each round of
- the loop. */
- while (words < endp)
- {
- md5_uint32 *cwp = correct_words;
- md5_uint32 A_save = A;
- md5_uint32 B_save = B;
- md5_uint32 C_save = C;
- md5_uint32 D_save = D;
-
- /* First round: using the given function, the context and a constant
- the next context is computed. Because the algorithms processing
- unit is a 32-bit word and it is determined to work on words in
- little endian byte order we perhaps have to change the byte order
- before the computation. To reduce the work for the next steps
- we store the swapped words in the array CORRECT_WORDS. */
-
-#define OP(a, b, c, d, s, T) \
- do \
- { \
- a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \
- ++words; \
- CYCLIC (a, s); \
- a += b; \
- } \
- while (0)
-
- /* It is unfortunate that C does not provide an operator for
- cyclic rotation. Hope the C compiler is smart enough. */
-#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
+ /* PAD bytes are used for padding to correct alignment. Note that always at least one byte is padded. */
+ pad = rest >= 56 ? 64 + 56 - rest : 56 - rest;
- /* Before we start, one word to the strange constants.
- They are defined in RFC 1321 as
-
- T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
- */
-
- /* Round 1. */
- OP (A, B, C, D, 7, 0xd76aa478);
- OP (D, A, B, C, 12, 0xe8c7b756);
- OP (C, D, A, B, 17, 0x242070db);
- OP (B, C, D, A, 22, 0xc1bdceee);
- OP (A, B, C, D, 7, 0xf57c0faf);
- OP (D, A, B, C, 12, 0x4787c62a);
- OP (C, D, A, B, 17, 0xa8304613);
- OP (B, C, D, A, 22, 0xfd469501);
- OP (A, B, C, D, 7, 0x698098d8);
- OP (D, A, B, C, 12, 0x8b44f7af);
- OP (C, D, A, B, 17, 0xffff5bb1);
- OP (B, C, D, A, 22, 0x895cd7be);
- OP (A, B, C, D, 7, 0x6b901122);
- OP (D, A, B, C, 12, 0xfd987193);
- OP (C, D, A, B, 17, 0xa679438e);
- OP (B, C, D, A, 22, 0x49b40821);
-
- /* For the second to fourth round we have the possibly swapped words
- in CORRECT_WORDS. Redefine the macro to take an additional first
- argument specifying the function to use. */
-#undef OP
-#define OP(f, a, b, c, d, k, s, T) \
- do \
- { \
- a += f (b, c, d) + correct_words[k] + T; \
- CYCLIC (a, s); \
- a += b; \
- } \
- while (0)
-
- /* Round 2. */
- OP (FG, A, B, C, D, 1, 5, 0xf61e2562);
- OP (FG, D, A, B, C, 6, 9, 0xc040b340);
- OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
- OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
- OP (FG, A, B, C, D, 5, 5, 0xd62f105d);
- OP (FG, D, A, B, C, 10, 9, 0x02441453);
- OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
- OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
- OP (FG, A, B, C, D, 9, 5, 0x21e1cde6);
- OP (FG, D, A, B, C, 14, 9, 0xc33707d6);
- OP (FG, C, D, A, B, 3, 14, 0xf4d50d87);
- OP (FG, B, C, D, A, 8, 20, 0x455a14ed);
- OP (FG, A, B, C, D, 13, 5, 0xa9e3e905);
- OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8);
- OP (FG, C, D, A, B, 7, 14, 0x676f02d9);
- OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
-
- /* Round 3. */
- OP (FH, A, B, C, D, 5, 4, 0xfffa3942);
- OP (FH, D, A, B, C, 8, 11, 0x8771f681);
- OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
- OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
- OP (FH, A, B, C, D, 1, 4, 0xa4beea44);
- OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9);
- OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60);
- OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
- OP (FH, A, B, C, D, 13, 4, 0x289b7ec6);
- OP (FH, D, A, B, C, 0, 11, 0xeaa127fa);
- OP (FH, C, D, A, B, 3, 16, 0xd4ef3085);
- OP (FH, B, C, D, A, 6, 23, 0x04881d05);
- OP (FH, A, B, C, D, 9, 4, 0xd9d4d039);
- OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
- OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
- OP (FH, B, C, D, A, 2, 23, 0xc4ac5665);
-
- /* Round 4. */
- OP (FI, A, B, C, D, 0, 6, 0xf4292244);
- OP (FI, D, A, B, C, 7, 10, 0x432aff97);
- OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
- OP (FI, B, C, D, A, 5, 21, 0xfc93a039);
- OP (FI, A, B, C, D, 12, 6, 0x655b59c3);
- OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92);
- OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
- OP (FI, B, C, D, A, 1, 21, 0x85845dd1);
- OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f);
- OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
- OP (FI, C, D, A, B, 6, 15, 0xa3014314);
- OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
- OP (FI, A, B, C, D, 4, 6, 0xf7537e82);
- OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
- OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
- OP (FI, B, C, D, A, 9, 21, 0xeb86d391);
-
- /* Add the starting values of the context. */
- A += A_save;
- B += B_save;
- C += C_save;
- D += D_save;
- }
+ /* Put length of buffer in *bits* in last eight bytes. */
+ *(md5_uint32 *) &restbuf[rest + pad] = (md5_uint32) SWAP(len << 3);
+ *(md5_uint32 *) &restbuf[rest + pad + 4] = (md5_uint32) SWAP(len >> 29);
- /* Put checksum in context given as argument. */
- ctx->A = A;
- ctx->B = B;
- ctx->C = C;
- ctx->D = D;
+ /* Process last bytes. */
+ md5_process_block(restbuf, rest + pad + 8, &ctx);
+
+ /* Put result in desired memory area. */
+ return md5_read_ctx(&ctx, resblock);
}
+char *md5_to_hexdigest(void *resblock, char r_hex_digest[33])
+{
+ static const char hex_map[17] = "0123456789abcdef";
+ const unsigned char *p;
+ char *q;
+ short len;
+
+ for (q = r_hex_digest, p = (const unsigned char *)resblock, len = 0; len < 16; ++p, ++len) {
+ const unsigned char c = *p;
+ *q++ = hex_map[c >> 4];
+ *q++ = hex_map[c & 15];
+ }
+ *q = '\0';
+
+ return r_hex_digest;
+}
diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c
index e00631fe996..0b89ec1f0d0 100644
--- a/source/blender/blenlib/intern/path_util.c
+++ b/source/blender/blenlib/intern/path_util.c
@@ -49,9 +49,9 @@
#include "GHOST_Path-api.h"
-#ifdef WIN32
-# include "MEM_guardedalloc.h"
+#include "MEM_guardedalloc.h"
+#ifdef WIN32
# include "utf_winfunc.h"
# include "utfconv.h"
# include <io.h>
@@ -66,6 +66,7 @@
# ifdef WITH_BINRELOC
# include "binreloc.h"
# endif
+# include <unistd.h> /* mkdtemp on OSX (and probably all *BSD?), not worth making specific check for this OS. */
#endif /* WIN32 */
/* local */
@@ -73,7 +74,8 @@
static char bprogname[FILE_MAX]; /* full path to program executable */
static char bprogdir[FILE_MAX]; /* full path to directory in which executable is located */
-static char btempdir[FILE_MAX]; /* temporary directory */
+static char btempdir_base[FILE_MAX]; /* persistent temporary directory */
+static char btempdir_session[FILE_MAX] = ""; /* volatile temporary directory */
/* implementation */
@@ -2319,14 +2321,21 @@ const char *BLI_program_dir(void)
*
* Also make sure the temp dir has a trailing slash
*
- * \param fullname The full path to the temp directory
+ * \param fullname The full path to the temporary temp directory
+ * \param basename The full path to the persistent temp directory (may be NULL)
* \param maxlen The size of the fullname buffer
* \param userdir Directory specified in user preferences
*/
-static void BLI_where_is_temp(char *fullname, const size_t maxlen, char *userdir)
+static void BLI_where_is_temp(char *fullname, char *basename, const size_t maxlen, char *userdir)
{
+ /* Clear existing temp dir, if needed. */
+ BLI_temp_dir_session_purge();
+
fullname[0] = '\0';
-
+ if (basename) {
+ basename[0] = '\0';
+ }
+
if (userdir && BLI_is_dir(userdir)) {
BLI_strncpy(fullname, userdir, maxlen);
}
@@ -2368,23 +2377,59 @@ static void BLI_where_is_temp(char *fullname, const size_t maxlen, char *userdir
}
#endif
}
+
+ /* Now that we have a valid temp dir, add system-generated unique sub-dir. */
+ if (basename) {
+ /* 'XXXXXX' is kind of tag to be replaced by mktemp-familly by an uuid. */
+ char *tmp_name = BLI_strdupcat(fullname, "blender_XXXXXX");
+ const size_t ln = strlen(tmp_name) + 1;
+ if (ln <= maxlen) {
+#ifdef WIN32
+ if (_mktemp_s(tmp_name, ln) == 0) {
+ BLI_dir_create_recursive(tmp_name);
+ }
+#else
+ mkdtemp(tmp_name);
+#endif
+ }
+ if (BLI_is_dir(tmp_name)) {
+ BLI_strncpy(basename, fullname, maxlen);
+ BLI_strncpy(fullname, tmp_name, maxlen);
+ BLI_add_slash(fullname);
+ }
+ else {
+ printf("Warning! Could not generate a temp file name for '%s', falling back to '%s'\n", tmp_name, fullname);
+ }
+
+ MEM_freeN(tmp_name);
+ }
}
/**
- * Sets btempdir to userdir if specified and is a valid directory, otherwise
+ * Sets btempdir_base to userdir if specified and is a valid directory, otherwise
* chooses a suitable OS-specific temporary directory.
+ * Sets btempdir_session to a mkdtemp-generated sub-dir of btempdir_base.
*/
-void BLI_init_temporary_dir(char *userdir)
+void BLI_temp_dir_init(char *userdir)
{
- BLI_where_is_temp(btempdir, FILE_MAX, userdir);
+ BLI_where_is_temp(btempdir_session, btempdir_base, FILE_MAX, userdir);
+;
}
/**
* Path to temporary directory (with trailing slash)
*/
-const char *BLI_temporary_dir(void)
+const char *BLI_temp_dir_session(void)
+{
+ return btempdir_session[0] ? btempdir_session : BLI_temp_dir_base();
+}
+
+/**
+ * Path to persistent temporary directory (with trailing slash)
+ */
+const char *BLI_temp_dir_base(void)
{
- return btempdir;
+ return btempdir_base;
}
/**
@@ -2392,7 +2437,17 @@ const char *BLI_temporary_dir(void)
*/
void BLI_system_temporary_dir(char *dir)
{
- BLI_where_is_temp(dir, FILE_MAX, NULL);
+ BLI_where_is_temp(dir, NULL, FILE_MAX, NULL);
+}
+
+/**
+ * Delete content of this instance's temp dir.
+ */
+void BLI_temp_dir_session_purge(void)
+{
+ if (btempdir_session[0] && BLI_is_dir(btempdir_session)) {
+ BLI_delete(btempdir_session, true, true);
+ }
}
#ifdef WITH_ICONV
diff --git a/source/blender/blenlib/intern/polyfill2d.c b/source/blender/blenlib/intern/polyfill2d.c
index fbe3c31dbc6..71cda92842a 100644
--- a/source/blender/blenlib/intern/polyfill2d.c
+++ b/source/blender/blenlib/intern/polyfill2d.c
@@ -53,8 +53,14 @@
/* avoid fan-fill topology */
#define USE_CLIP_EVEN
#define USE_CONVEX_SKIP
+/* sweep back-and-forth about convex ears (avoids lop-sided fans) */
+#define USE_CLIP_SWEEP
// #define USE_CONVEX_SKIP_TEST
+#ifdef USE_CONVEX_SKIP
+# define USE_KDTREE
+#endif
+
// #define DEBUG_TIME
#ifdef DEBUG_TIME
# include "PIL_time_utildefines.h"
@@ -62,6 +68,52 @@
typedef signed char eSign;
+
+#ifdef USE_KDTREE
+/**
+ * This is a single purpose KDTree based on BLI_kdtree with some modifications
+ * to better suit polyfill2d.
+ *
+ *
+ * - #KDTreeNode2D is kept small (only 16 bytes),
+ * by not storing coords in the nodes and using index values rather then pointers
+ * to reference neg/pos values.
+ *
+ * - #kdtree2d_isect_tri is the only function currently used.
+ * This simply intersects a triangle with the kdtree points.
+ *
+ * - the KDTree is only built & used when the polygon is concave.
+ */
+
+typedef bool axis_t;
+
+/* use for sorting */
+typedef struct KDTreeNode2D_head {
+ unsigned int neg, pos;
+ unsigned int index;
+} KDTreeNode2D_head;
+
+typedef struct KDTreeNode2D {
+ unsigned int neg, pos;
+ unsigned int index;
+ axis_t axis; /* range is only (0-1) */
+ unsigned short flag;
+ unsigned int parent;
+} KDTreeNode2D;
+
+struct KDTree2D {
+ KDTreeNode2D *nodes;
+ const float (*coords)[2];
+ unsigned int root;
+ unsigned int totnode;
+ unsigned int *nodes_map; /* index -> node lookup */
+};
+
+struct KDRange2D {
+ float min, max;
+};
+#endif /* USE_KDTREE */
+
enum {
CONCAVE = -1,
TANGENTIAL = 0,
@@ -69,11 +121,10 @@ enum {
};
typedef struct PolyFill {
- unsigned int *indices; /* vertex aligned */
+ struct PolyIndex *indices; /* vertex aligned */
const float (*coords)[2];
unsigned int coords_tot;
- eSign *coords_sign;
#ifdef USE_CONVEX_SKIP
unsigned int coords_tot_concave;
#endif
@@ -81,22 +132,37 @@ typedef struct PolyFill {
/* A polygon with n vertices has a triangulation of n-2 triangles. */
unsigned int (*tris)[3];
unsigned int tris_tot;
+
+#ifdef USE_KDTREE
+ struct KDTree2D kdtree;
+#endif
} PolyFill;
+/* circular linklist */
+typedef struct PolyIndex {
+ struct PolyIndex *next, *prev;
+ unsigned int index;
+ eSign sign;
+} PolyIndex;
+
+
/* based on libgdx 2013-11-28, apache 2.0 licensed */
-static eSign pf_coord_sign_calc(PolyFill *pf, unsigned int index);
-static unsigned int pf_index_prev(const PolyFill *pf, const unsigned int index);
-static unsigned int pf_index_next(const PolyFill *pf, unsigned index);
+static void pf_coord_sign_calc(PolyFill *pf, PolyIndex *pi);
+static PolyIndex *pf_ear_tip_find(
+ PolyFill *pf
#ifdef USE_CLIP_EVEN
-static unsigned int pf_ear_tip_find(PolyFill *pf, const unsigned int index_offset);
-#else
-static unsigned int pf_ear_tip_find(PolyFill *pf);
+ , PolyIndex *pi_ear_init
#endif
-static bool pf_ear_tip_check(PolyFill *pf, const unsigned int index_ear_tip);
-static void pf_ear_tip_cut(PolyFill *pf, unsigned int index_ear_tip);
+#ifdef USE_CLIP_SWEEP
+ , bool reverse
+#endif
+ );
+
+static bool pf_ear_tip_check(PolyFill *pf, PolyIndex *pi_ear_tip);
+static void pf_ear_tip_cut(PolyFill *pf, PolyIndex *pi_ear_tip);
BLI_INLINE eSign signum_i(float a)
@@ -128,122 +194,441 @@ static eSign span_tri_v2_sign(const float v1[2], const float v2[2], const float
return signum_i(area_tri_signed_v2_alt_2x(v3, v2, v1));
}
+
+#ifdef USE_KDTREE
+#define KDNODE_UNSET ((unsigned int)-1)
+
+enum {
+ KDNODE_FLAG_REMOVED = (1 << 0),
+};
+
+static void kdtree2d_new(
+ struct KDTree2D *tree,
+ unsigned int tot,
+ const float (*coords)[2])
+{
+ /* set by caller */
+ // tree->nodes = nodes;
+ tree->coords = coords;
+ tree->root = KDNODE_UNSET;
+ tree->totnode = tot;
+}
+
+/**
+ * no need for kdtree2d_insert, since we know the coords array.
+ */
+static void kdtree2d_init(
+ struct KDTree2D *tree,
+ const unsigned int coords_tot,
+ const PolyIndex *indices)
+{
+ KDTreeNode2D *node;
+ unsigned int i;
+
+ for (i = 0, node = tree->nodes; i < coords_tot; i++) {
+ if (indices[i].sign != CONVEX) {
+ node->neg = node->pos = KDNODE_UNSET;
+ node->index = indices[i].index;
+ node->axis = 0;
+ node->flag = 0;
+ node++;
+ }
+ }
+
+ BLI_assert(tree->totnode == (unsigned int)(node - tree->nodes));
+}
+
+static unsigned int kdtree2d_balance_recursive(
+ KDTreeNode2D *nodes, unsigned int totnode, axis_t axis,
+ const float (*coords)[2], const unsigned int ofs)
+{
+ KDTreeNode2D *node;
+ unsigned int neg, pos, median, i, j;
+
+ if (totnode <= 0) {
+ return KDNODE_UNSET;
+ }
+ else if (totnode == 1) {
+ return 0 + ofs;
+ }
+
+ /* quicksort style sorting around median */
+ neg = 0;
+ pos = totnode - 1;
+ median = totnode / 2;
+
+ while (pos > neg) {
+ const float co = coords[nodes[pos].index][axis];
+ i = neg - 1;
+ j = pos;
+
+ while (1) {
+ while (coords[nodes[++i].index][axis] < co) ;
+ while (coords[nodes[--j].index][axis] > co && j > neg) ;
+
+ if (i >= j) {
+ break;
+ }
+ SWAP(KDTreeNode2D_head, *(KDTreeNode2D_head *)&nodes[i], *(KDTreeNode2D_head *)&nodes[j]);
+ }
+
+ SWAP(KDTreeNode2D_head, *(KDTreeNode2D_head *)&nodes[i], *(KDTreeNode2D_head *)&nodes[pos]);
+ if (i >= median) {
+ pos = i - 1;
+ }
+ if (i <= median) {
+ neg = i + 1;
+ }
+ }
+
+ /* set node and sort subnodes */
+ node = &nodes[median];
+ node->axis = axis;
+ axis = !axis;
+ node->neg = kdtree2d_balance_recursive(nodes, median, axis, coords, ofs);
+ node->pos = kdtree2d_balance_recursive(&nodes[median + 1], (totnode - (median + 1)), axis, coords, (median + 1) + ofs);
+
+ return median + ofs;
+}
+
+static void kdtree2d_balance(
+ struct KDTree2D *tree)
+{
+ tree->root = kdtree2d_balance_recursive(tree->nodes, tree->totnode, 0, tree->coords, 0);
+}
+
+
+static void kdtree2d_init_mapping(
+ struct KDTree2D *tree)
+{
+ unsigned int i;
+ KDTreeNode2D *node;
+
+ for (i = 0, node = tree->nodes; i < tree->totnode; i++, node++) {
+ if (node->neg != KDNODE_UNSET) {
+ tree->nodes[node->neg].parent = i;
+ }
+ if (node->pos != KDNODE_UNSET) {
+ tree->nodes[node->pos].parent = i;
+ }
+
+ /* build map */
+ BLI_assert(tree->nodes_map[node->index] == KDNODE_UNSET);
+ tree->nodes_map[node->index] = i;
+ }
+
+ tree->nodes[tree->root].parent = KDNODE_UNSET;
+}
+
+static void kdtree2d_node_remove(
+ struct KDTree2D *tree,
+ unsigned int index)
+{
+ unsigned int node_index = tree->nodes_map[index];
+ KDTreeNode2D *node;
+
+ if (node_index == KDNODE_UNSET) {
+ return;
+ }
+ else {
+ tree->nodes_map[index] = KDNODE_UNSET;
+ }
+
+ node = &tree->nodes[node_index];
+ tree->totnode -= 1;
+
+ BLI_assert((node->flag & KDNODE_FLAG_REMOVED) == 0);
+ node->flag |= KDNODE_FLAG_REMOVED;
+
+ while ((node->neg == KDNODE_UNSET) &&
+ (node->pos == KDNODE_UNSET) &&
+ (node->parent != KDNODE_UNSET))
+ {
+ KDTreeNode2D *node_parent = &tree->nodes[node->parent];
+
+ BLI_assert((unsigned int)(node - tree->nodes) == node_index);
+ if (node_parent->neg == node_index) {
+ node_parent->neg = KDNODE_UNSET;
+ }
+ else {
+ BLI_assert(node_parent->pos == node_index);
+ node_parent->pos = KDNODE_UNSET;
+ }
+
+ if (node_parent->flag & KDNODE_FLAG_REMOVED) {
+ node_index = node->parent;
+ node = node_parent;
+ }
+ else {
+ break;
+ }
+ }
+}
+
+static bool kdtree2d_isect_tri_recursive(
+ const struct KDTree2D *tree,
+ const unsigned int tri_index[3],
+ const float *tri_coords[3],
+ const float tri_center[2],
+ const struct KDRange2D bounds[2],
+ const KDTreeNode2D *node)
+{
+ const float *co = tree->coords[node->index];
+
+ /* bounds then triangle intersect */
+ if ((node->flag & KDNODE_FLAG_REMOVED) == 0) {
+ /* bounding box test first */
+ if ((co[0] >= bounds[0].min) &&
+ (co[0] <= bounds[0].max) &&
+ (co[1] >= bounds[1].min) &&
+ (co[1] <= bounds[1].max))
+ {
+ if ((span_tri_v2_sign(tri_coords[0], tri_coords[1], co) != CONCAVE) &&
+ (span_tri_v2_sign(tri_coords[1], tri_coords[2], co) != CONCAVE) &&
+ (span_tri_v2_sign(tri_coords[2], tri_coords[0], co) != CONCAVE))
+ {
+ if (!ELEM3(node->index, tri_index[0], tri_index[1], tri_index[2])) {
+ return true;
+ }
+ }
+ }
+ }
+
+#define KDTREE2D_ISECT_TRI_RECURSE_NEG \
+ (((node->neg != KDNODE_UNSET) && (co[node->axis] > bounds[node->axis].min)) && \
+ (kdtree2d_isect_tri_recursive(tree, tri_index, tri_coords, tri_center, bounds, \
+ &tree->nodes[node->neg])))
+#define KDTREE2D_ISECT_TRI_RECURSE_POS \
+ (((node->pos != KDNODE_UNSET) && (co[node->axis] < bounds[node->axis].max)) && \
+ (kdtree2d_isect_tri_recursive(tree, tri_index, tri_coords, tri_center, bounds, \
+ &tree->nodes[node->pos])))
+
+ if (tri_center[node->axis] > co[node->axis]) {
+ if (KDTREE2D_ISECT_TRI_RECURSE_POS) {
+ return true;
+ }
+ if (KDTREE2D_ISECT_TRI_RECURSE_NEG) {
+ return true;
+ }
+ }
+ else {
+ if (KDTREE2D_ISECT_TRI_RECURSE_NEG) {
+ return true;
+ }
+ if (KDTREE2D_ISECT_TRI_RECURSE_POS) {
+ return true;
+ }
+ }
+
+#undef KDTREE2D_ISECT_TRI_RECURSE_NEG
+#undef KDTREE2D_ISECT_TRI_RECURSE_POS
+
+ BLI_assert(node->index != KDNODE_UNSET);
+
+ return false;
+}
+
+static bool kdtree2d_isect_tri(
+ struct KDTree2D *tree,
+ const unsigned int ind[3])
+{
+ const float *vs[3];
+ unsigned int i;
+ struct KDRange2D bounds[2] = {
+ {FLT_MAX, -FLT_MAX},
+ {FLT_MAX, -FLT_MAX},
+ };
+ float tri_center[2] = {0.0f, 0.0f};
+
+ for (i = 0; i < 3; i++) {
+ vs[i] = tree->coords[ind[i]];
+
+ add_v2_v2(tri_center, vs[i]);
+
+ CLAMP_MAX(bounds[0].min, vs[i][0]);
+ CLAMP_MIN(bounds[0].max, vs[i][0]);
+ CLAMP_MAX(bounds[1].min, vs[i][1]);
+ CLAMP_MIN(bounds[1].max, vs[i][1]);
+ }
+
+ mul_v2_fl(tri_center, 1.0f / 3.0f);
+
+ return kdtree2d_isect_tri_recursive(tree, ind, vs, tri_center, bounds, &tree->nodes[tree->root]);
+}
+
+#endif /* USE_KDTREE */
+
+
static unsigned int *pf_tri_add(PolyFill *pf)
{
return pf->tris[pf->tris_tot++];
}
-static void pf_coord_remove(PolyFill *pf, const unsigned int index)
+static void pf_coord_remove(PolyFill *pf, PolyIndex *pi)
{
- ARRAY_DELETE(pf->indices, index, 1, pf->coords_tot);
- ARRAY_DELETE(pf->coords_sign, index, 1, pf->coords_tot);
+#ifdef USE_KDTREE
+ /* avoid double lookups, since convex coords are ignored when testing intersections */
+ if (pf->kdtree.totnode) {
+ kdtree2d_node_remove(&pf->kdtree, pi->index);
+ }
+#endif
+
+ pi->next->prev = pi->prev;
+ pi->prev->next = pi->next;
+
+ if (UNLIKELY(pf->indices == pi)) {
+ pf->indices = pi->next;
+ }
+#ifdef DEBUG
+ pi->index = (unsigned int)-1;
+ pi->next = pi->prev = NULL;
+#endif
+
pf->coords_tot -= 1;
}
static void pf_triangulate(PolyFill *pf)
{
/* localize */
- eSign *coords_sign = pf->coords_sign;
-
- unsigned int index_ear_tip = 0;
+ PolyIndex *pi_ear;
+#ifdef USE_CLIP_EVEN
+ PolyIndex *pi_ear_init = pf->indices;
+#endif
+#ifdef USE_CLIP_SWEEP
+ bool reverse = false;
+#endif
while (pf->coords_tot > 3) {
- unsigned int i_prev, i_next;
-
-#ifdef USE_CONVEX_SKIP
+ PolyIndex *pi_prev, *pi_next;
eSign sign_orig_prev, sign_orig_next;
-#endif
+ pi_ear = pf_ear_tip_find(
+ pf
+#ifdef USE_CLIP_EVEN
+ , pi_ear_init
+#endif
+#ifdef USE_CLIP_SWEEP
+ , reverse
+#endif
+ );
+#ifdef USE_CLIP_SWEEP
#ifdef USE_CLIP_EVEN
- index_ear_tip = pf_ear_tip_find(pf, index_ear_tip);
+ if (pi_ear != pi_ear_init) {
+ reverse = !reverse;
+ }
#else
- index_ear_tip = pf_ear_tip_find(pf);
+ if (pi_ear != pf->indices) {
+ reverse = !reverse;
+ }
+#endif
#endif
#ifdef USE_CONVEX_SKIP
- if (coords_sign[index_ear_tip] != CONVEX) {
+ if (pi_ear->sign != CONVEX) {
pf->coords_tot_concave -= 1;
}
#endif
- pf_ear_tip_cut(pf, index_ear_tip);
+ pi_prev = pi_ear->prev;
+ pi_next = pi_ear->next;
+
+ pf_ear_tip_cut(pf, pi_ear);
/* The type of the two vertices adjacent to the clipped vertex may have changed. */
- i_prev = pf_index_prev(pf, index_ear_tip);
- i_next = (index_ear_tip == pf->coords_tot) ? 0 : index_ear_tip;
+ sign_orig_prev = pi_prev->sign;
+ sign_orig_next = pi_next->sign;
+ /* check if any verts became convex the (else if)
+ * case is highly unlikely but may happen with degenerate polygons */
+ if (sign_orig_prev != CONVEX) {
+ pf_coord_sign_calc(pf, pi_prev);
#ifdef USE_CONVEX_SKIP
- sign_orig_prev = coords_sign[i_prev];
- sign_orig_next = coords_sign[i_next];
+ if (pi_prev->sign == CONVEX) {
+ pf->coords_tot_concave -= 1;
+#ifdef USE_KDTREE
+ kdtree2d_node_remove(&pf->kdtree, pi_prev->index);
#endif
-
- coords_sign[i_prev] = pf_coord_sign_calc(pf, i_prev);
- coords_sign[i_next] = pf_coord_sign_calc(pf, i_next);
-
+ }
+#endif
+ }
+ if (sign_orig_next != CONVEX) {
+ pf_coord_sign_calc(pf, pi_next);
#ifdef USE_CONVEX_SKIP
- /* check if any verts became convex the (else if)
- * case is highly unlikely but may happen with degenerate polygons */
- if ((sign_orig_prev != CONVEX) && (coords_sign[i_prev] == CONVEX)) pf->coords_tot_concave -= 1;
- else if (UNLIKELY((sign_orig_prev == CONVEX) && (coords_sign[i_prev] != CONVEX))) pf->coords_tot_concave += 1;
-
- if ((sign_orig_next != CONVEX) && (coords_sign[i_next] == CONVEX)) pf->coords_tot_concave -= 1;
- else if (UNLIKELY((sign_orig_next == CONVEX) && (coords_sign[i_next] != CONVEX))) pf->coords_tot_concave += 1;
+ if (pi_next->sign == CONVEX) {
+ pf->coords_tot_concave -= 1;
+#ifdef USE_KDTREE
+ kdtree2d_node_remove(&pf->kdtree, pi_next->index);
+#endif
+ }
#endif
+ }
#ifdef USE_CLIP_EVEN
- index_ear_tip = i_prev;
+#ifdef USE_CLIP_SWEEP
+ pi_ear_init = reverse ? pi_next->next : pi_prev->prev;
+#else
+ pi_ear_init = pi_next->next;
+#endif
#endif
+
}
if (pf->coords_tot == 3) {
unsigned int *tri = pf_tri_add(pf);
- tri[0] = pf->indices[0];
- tri[1] = pf->indices[1];
- tri[2] = pf->indices[2];
+ pi_ear = pf->indices;
+ tri[0] = pi_ear->index; pi_ear = pi_ear->next;
+ tri[1] = pi_ear->index; pi_ear = pi_ear->next;
+ tri[2] = pi_ear->index;
}
}
/**
* \return CONCAVE, TANGENTIAL or CONVEX
*/
-static eSign pf_coord_sign_calc(PolyFill *pf, unsigned int index)
+static void pf_coord_sign_calc(PolyFill *pf, PolyIndex *pi)
{
/* localize */
- unsigned int *indices = pf->indices;
const float (*coords)[2] = pf->coords;
- return span_tri_v2_sign(
- coords[indices[pf_index_prev(pf, index)]],
- coords[indices[index]],
- coords[indices[pf_index_next(pf, index)]]);
+ pi->sign = span_tri_v2_sign(
+ coords[pi->prev->index],
+ coords[pi->index],
+ coords[pi->next->index]);
}
+static PolyIndex *pf_ear_tip_find(
+ PolyFill *pf
#ifdef USE_CLIP_EVEN
-static unsigned int pf_ear_tip_find(PolyFill *pf, const unsigned int index_offset)
-#else
-static unsigned int pf_ear_tip_find(PolyFill *pf)
+ , PolyIndex *pi_ear_init
#endif
+#ifdef USE_CLIP_SWEEP
+ , bool reverse
+#endif
+ )
{
/* localize */
- const eSign *coords_sign = pf->coords_sign;
const unsigned int coords_tot = pf->coords_tot;
+ PolyIndex *pi_ear;
unsigned int i;
+#ifdef USE_CLIP_EVEN
+ pi_ear = pi_ear_init;
+#else
+ pi_ear = pf->indices;
+#endif
i = coords_tot;
while (i--) {
-#ifdef USE_CLIP_EVEN
- unsigned int j = (i + index_offset) % coords_tot;
- if (pf_ear_tip_check(pf, j)) {
- return j;
+ if (pf_ear_tip_check(pf, pi_ear)) {
+ return pi_ear;
}
+#ifdef USE_CLIP_SWEEP
+ pi_ear = reverse ? pi_ear->prev : pi_ear->next;
#else
- if (pf_ear_tip_check(pf, i)) {
- return i;
- }
+ pi_ear = pi_ear->next;
#endif
}
@@ -256,36 +641,35 @@ static unsigned int pf_ear_tip_find(PolyFill *pf)
* Return a convex or tangential vertex if one exists.
*/
- i = coords_tot;
- while (i--) {
#ifdef USE_CLIP_EVEN
- unsigned int j = (i + index_offset) % coords_tot;
- if (coords_sign[j] != CONCAVE) {
- return j;
- }
+ pi_ear = pi_ear_init;
#else
- if (coords_sign[i] != CONCAVE) {
- return i;
- }
+ pi_ear = pf->indices;
#endif
+
+ i = coords_tot;
+ while (i--) {
+ if (pi_ear->sign != CONCAVE) {
+ return pi_ear;
+ }
+ pi_ear = pi_ear->next;
}
/* If all vertices are concave, just return the last one. */
- return coords_tot - 1;
+ return pi_ear;
}
-static bool pf_ear_tip_check(PolyFill *pf, const unsigned int index_ear_tip)
+static bool pf_ear_tip_check(PolyFill *pf, PolyIndex *pi_ear_tip)
{
/* localize */
- const unsigned int *indices = pf->indices;
+ PolyIndex *pi_curr;
const float (*coords)[2] = pf->coords;
- const eSign *coords_sign = pf->coords_sign;
-
- unsigned int i_prev, i_curr, i_next;
+#ifndef USE_KDTREE
const float *v1, *v2, *v3;
+#endif
-#ifdef USE_CONVEX_SKIP
+#if defined(USE_CONVEX_SKIP) && !defined(USE_KDTREE)
unsigned int coords_tot_concave_checked = 0;
#endif
@@ -298,7 +682,7 @@ static bool pf_ear_tip_check(PolyFill *pf, const unsigned int index_ear_tip)
unsigned int coords_tot_concave_test = 0;
unsigned int i = pf->coords_tot;
while (i--) {
- if (coords_sign[i] != CONVEX) {
+ if (coords_sign[indices[i]] != CONVEX) {
coords_tot_concave_test += 1;
}
}
@@ -312,25 +696,37 @@ static bool pf_ear_tip_check(PolyFill *pf, const unsigned int index_ear_tip)
}
#endif
- if (UNLIKELY(coords_sign[index_ear_tip] == CONCAVE)) {
+ if (UNLIKELY(pi_ear_tip->sign == CONCAVE)) {
return false;
}
- i_prev = pf_index_prev(pf, index_ear_tip);
- i_next = pf_index_next(pf, index_ear_tip);
+#ifdef USE_KDTREE
+ {
+ const unsigned int ind[3] = {
+ pi_ear_tip->index,
+ pi_ear_tip->next->index,
+ pi_ear_tip->prev->index};
- v1 = coords[indices[i_prev]];
- v2 = coords[indices[index_ear_tip]];
- v3 = coords[indices[i_next]];
+ if (kdtree2d_isect_tri(&pf->kdtree, ind)) {
+ return false;
+ }
+ }
+ (void)pi_curr;
+ (void)coords;
+#else
+
+ v1 = coords[pi_ear_tip->prev->index];
+ v2 = coords[pi_ear_tip->index];
+ v3 = coords[pi_ear_tip->next->index];
/* Check if any point is inside the triangle formed by previous, current and next vertices.
* Only consider vertices that are not part of this triangle, or else we'll always find one inside. */
- for (i_curr = pf_index_next(pf, i_next); i_curr != i_prev; i_curr = pf_index_next(pf, i_curr)) {
+ for (pi_curr = pi_ear_tip->next->next; pi_curr != pi_ear_tip->prev; pi_curr = pi_curr->next) {
/* Concave vertices can obviously be inside the candidate ear, but so can tangential vertices
* if they coincide with one of the triangle's vertices. */
- if (coords_sign[i_curr] != CONVEX) {
- const float *v = coords[indices[i_curr]];
+ if (pi_curr->sign != CONVEX) {
+ const float *v = coords[pi_curr->index];
/* Because the polygon has clockwise winding order,
* the area sign will be positive if the point is strictly inside.
* It will be 0 on the edge, which we want to include as well. */
@@ -352,28 +748,20 @@ static bool pf_ear_tip_check(PolyFill *pf, const unsigned int index_ear_tip)
#endif
}
}
+#endif /* USE_KDTREE */
+
return true;
}
-static void pf_ear_tip_cut(PolyFill *pf, unsigned int index_ear_tip)
+static void pf_ear_tip_cut(PolyFill *pf, PolyIndex *pi_ear_tip)
{
unsigned int *tri = pf_tri_add(pf);
- tri[0] = pf->indices[pf_index_prev(pf, index_ear_tip)];
- tri[1] = pf->indices[index_ear_tip];
- tri[2] = pf->indices[pf_index_next(pf, index_ear_tip)];
-
- pf_coord_remove(pf, index_ear_tip);
-}
-
-static unsigned int pf_index_prev(const PolyFill *pf, const unsigned int index)
-{
- return (index ? index : pf->coords_tot) - 1;
-}
+ tri[0] = pi_ear_tip->prev->index;
+ tri[1] = pi_ear_tip->index;
+ tri[2] = pi_ear_tip->next->index;
-static unsigned int pf_index_next(const PolyFill *pf, unsigned index)
-{
- return (index + 1) % pf->coords_tot;
+ pf_coord_remove(pf, pi_ear_tip);
}
/**
@@ -383,100 +771,169 @@ static unsigned int pf_index_next(const PolyFill *pf, unsigned index)
* \return triples of triangle indices in clockwise order.
* Note the returned array is reused for later calls to the same method.
*/
-void BLI_polyfill_calc_ex(
+static void polyfill_prepare(
+ PolyFill *pf,
const float (*coords)[2],
const unsigned int coords_tot,
+ int coords_sign,
unsigned int (*r_tris)[3],
-
- unsigned int *r_indices, eSign *r_coords_sign)
+ PolyIndex *r_indices)
{
- PolyFill pf;
-
/* localize */
- unsigned int *indices = r_indices;
- eSign *coords_sign = r_coords_sign;
+ PolyIndex *indices = r_indices;
unsigned int i;
-#ifdef DEBUG_TIME
- TIMEIT_START(polyfill2d);
-#endif
-
/* assign all polyfill members here */
- pf.indices = r_indices;
- pf.coords = coords;
- pf.coords_tot = coords_tot;
- pf.coords_sign = r_coords_sign;
+ pf->indices = r_indices;
+ pf->coords = coords;
+ pf->coords_tot = coords_tot;
#ifdef USE_CONVEX_SKIP
- pf.coords_tot_concave = 0;
+ pf->coords_tot_concave = 0;
#endif
- pf.tris = r_tris;
- pf.tris_tot = 0;
+ pf->tris = r_tris;
+ pf->tris_tot = 0;
- if ((coords_tot < 3) ||
- cross_poly_v2(coords, coords_tot) > 0.0f)
- {
+ if (coords_sign == 0) {
+ coords_sign = (cross_poly_v2(coords, coords_tot) >= 0.0f) ? 1 : -1;
+ }
+ else {
+ /* chech we're passing in correcty args */
+#ifndef NDEBUG
+ if (coords_sign == 1) {
+ BLI_assert(cross_poly_v2(coords, coords_tot) >= 0.0f);
+ }
+ else {
+ BLI_assert(cross_poly_v2(coords, coords_tot) <= 0.0f);
+ }
+#endif
+ }
+
+ if (coords_sign == 1) {
for (i = 0; i < coords_tot; i++) {
- indices[i] = i;
+ indices[i].next = &indices[i + 1];
+ indices[i].prev = &indices[i - 1];
+ indices[i].index = i;
}
}
else {
/* reversed */
unsigned int n = coords_tot - 1;
for (i = 0; i < coords_tot; i++) {
- indices[i] = (n - i);
+ indices[i].next = &indices[i + 1];
+ indices[i].prev = &indices[i - 1];
+ indices[i].index = (n - i);
}
}
+ indices[0].prev = &indices[coords_tot - 1];
+ indices[coords_tot - 1].next = &indices[0];
for (i = 0; i < coords_tot; i++) {
- coords_sign[i] = pf_coord_sign_calc(&pf, i);
+ PolyIndex *pi = &indices[i];
+ pf_coord_sign_calc(pf, pi);
#ifdef USE_CONVEX_SKIP
- if (coords_sign[i] != CONVEX) {
- pf.coords_tot_concave += 1;
+ if (pi->sign != CONVEX) {
+ pf->coords_tot_concave += 1;
}
#endif
}
+}
- pf_triangulate(&pf);
-
-#ifdef DEBUG_TIME
- TIMEIT_END(polyfill2d);
+static void polyfill_calc(
+ PolyFill *pf)
+{
+#ifdef USE_KDTREE
+#ifdef USE_CONVEX_SKIP
+ if (pf->coords_tot_concave)
#endif
+ {
+ kdtree2d_new(&pf->kdtree, pf->coords_tot_concave, pf->coords);
+ kdtree2d_init(&pf->kdtree, pf->coords_tot, pf->indices);
+ kdtree2d_balance(&pf->kdtree);
+ kdtree2d_init_mapping(&pf->kdtree);
+ }
+#endif
+
+ pf_triangulate(pf);
}
void BLI_polyfill_calc_arena(
const float (*coords)[2],
const unsigned int coords_tot,
+ const int coords_sign,
unsigned int (*r_tris)[3],
struct MemArena *arena)
{
- unsigned int *indices = BLI_memarena_alloc(arena, sizeof(*indices) * coords_tot);
- eSign *coords_sign = BLI_memarena_alloc(arena, sizeof(*coords_sign) * coords_tot);
+ PolyFill pf;
+ PolyIndex *indices = BLI_memarena_alloc(arena, sizeof(*indices) * coords_tot);
+
+#ifdef DEBUG_TIME
+ TIMEIT_START(polyfill2d);
+#endif
- BLI_polyfill_calc_ex(
- coords, coords_tot,
+ polyfill_prepare(
+ &pf,
+ coords, coords_tot, coords_sign,
r_tris,
/* cache */
+ indices);
- indices, coords_sign);
+#ifdef USE_KDTREE
+ if (pf.coords_tot_concave) {
+ pf.kdtree.nodes = BLI_memarena_alloc(arena, sizeof(*pf.kdtree.nodes) * pf.coords_tot_concave);
+ pf.kdtree.nodes_map = memset(BLI_memarena_alloc(arena, sizeof(*pf.kdtree.nodes_map) * coords_tot),
+ 0xff, sizeof(*pf.kdtree.nodes_map) * coords_tot);
+ }
+ else {
+ pf.kdtree.totnode = 0;
+ }
+#endif
+
+ polyfill_calc(&pf);
- /* indices & coords_sign are no longer needed,
+ /* indices are no longer needed,
* caller can clear arena */
+
+#ifdef DEBUG_TIME
+ TIMEIT_END(polyfill2d);
+#endif
}
void BLI_polyfill_calc(
const float (*coords)[2],
const unsigned int coords_tot,
+ const int coords_sign,
unsigned int (*r_tris)[3])
{
- unsigned int *indices = BLI_array_alloca(indices, coords_tot);
- eSign *coords_sign = BLI_array_alloca(coords_sign, coords_tot);
+ PolyFill pf;
+ PolyIndex *indices = BLI_array_alloca(indices, coords_tot);
+
+#ifdef DEBUG_TIME
+ TIMEIT_START(polyfill2d);
+#endif
- BLI_polyfill_calc_ex(
- coords, coords_tot,
+ polyfill_prepare(
+ &pf,
+ coords, coords_tot, coords_sign,
r_tris,
/* cache */
+ indices);
- indices, coords_sign);
+#ifdef USE_KDTREE
+ if (pf.coords_tot_concave) {
+ pf.kdtree.nodes = BLI_array_alloca(pf.kdtree.nodes, pf.coords_tot_concave);
+ pf.kdtree.nodes_map = memset(BLI_array_alloca(pf.kdtree.nodes_map, coords_tot),
+ 0xff, sizeof(*pf.kdtree.nodes_map) * coords_tot);
+ }
+ else {
+ pf.kdtree.totnode = 0;
+ }
+#endif
+
+ polyfill_calc(&pf);
+
+#ifdef DEBUG_TIME
+ TIMEIT_END(polyfill2d);
+#endif
}
diff --git a/source/blender/blenlib/intern/rand.c b/source/blender/blenlib/intern/rand.c
index a5d6e2f7c1e..410f98897ce 100644
--- a/source/blender/blenlib/intern/rand.c
+++ b/source/blender/blenlib/intern/rand.c
@@ -117,6 +117,13 @@ float BLI_rng_get_float(RNG *rng)
return (float) BLI_rng_get_int(rng) / 0x80000000;
}
+void BLI_rng_get_float_unit_v2(RNG *rng, float v[2])
+{
+ float a = (float)(M_PI * 2.0) * BLI_rng_get_float(rng);
+ v[0] = cosf(a);
+ v[1] = sinf(a);
+}
+
void BLI_rng_get_float_unit_v3(RNG *rng, float v[3])
{
float r;
diff --git a/source/blender/blenlib/intern/sort.c b/source/blender/blenlib/intern/sort.c
index a1b7296feb6..9fad7505f79 100644
--- a/source/blender/blenlib/intern/sort.c
+++ b/source/blender/blenlib/intern/sort.c
@@ -32,10 +32,13 @@
#include <stdlib.h>
+#ifndef __GLIBC__
+
#include "BLI_utildefines.h"
#include "BLI_sort.h"
+/* note: modified to use glibc arg order for callback */
/* **** qsort based on FreeBSD source (libkern\qsort.c) **** */
BLI_INLINE char *med3(char *, char *, char *, BLI_sort_cmp_t, void *);
BLI_INLINE void swapfunc(char *, char *, int, int);
@@ -72,7 +75,7 @@ BLI_INLINE void swapfunc(char *a, char *b, int n, int swaptype)
swapfunc(a, b, es, swaptype)
#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype)
-#define CMP(t, x, y) (cmp((t), (x), (y)))
+#define CMP(t, x, y) (cmp((x), (y), (t)))
BLI_INLINE char *med3(char *a, char *b, char *c, BLI_sort_cmp_t cmp, void *thunk)
{
@@ -86,7 +89,7 @@ BLI_INLINE char *med3(char *a, char *b, char *c, BLI_sort_cmp_t cmp, void *thunk
*
* \note Follows BSD arg order (incompatible with glibc).
*/
-void BLI_qsort_r(void *a, size_t n, size_t es, void *thunk, BLI_sort_cmp_t cmp)
+void BLI_qsort_r(void *a, size_t n, size_t es, BLI_sort_cmp_t cmp, void *thunk)
{
char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
int d, r, swaptype, swap_cnt;
@@ -163,7 +166,7 @@ loop:
r = min(pd - pc, pn - pd - es);
vecswap(pb, pn - r, r);
if ((r = pb - pa) > es)
- BLI_qsort_r(a, r / es, es, thunk, cmp);
+ BLI_qsort_r(a, r / es, es, cmp, thunk);
if ((r = pd - pc) > es) {
/* Iterate rather than recurse to save stack space */
a = pn - r;
@@ -171,3 +174,5 @@ loop:
goto loop;
}
}
+
+#endif /* __GLIBC__ */
diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c
index a5de1072372..453b0cc939f 100644
--- a/source/blender/blenlib/intern/storage.c
+++ b/source/blender/blenlib/intern/storage.c
@@ -255,22 +255,7 @@ static void bli_builddir(struct BuildDirCtx *dir_ctx, const char *dirname)
file->relname = dlink->name;
file->path = BLI_strdupcat(dirname, dlink->name);
BLI_join_dirfile(fullname, sizeof(fullname), dirname, dlink->name);
-// use 64 bit file size, only needed for WIN32 and WIN64.
-// Excluding other than current MSVC compiler until able to test
-#ifdef WIN32
- {
- wchar_t *name_16 = alloc_utf16_from_8(fullname, 0);
-#if defined(_MSC_VER) && (_MSC_VER >= 1500)
- _wstat64(name_16, &file->s);
-#elif defined(__MINGW32__)
- _stati64(fullname, &file->s);
-#endif
- free(name_16);
- }
-
-#else
- stat(fullname, &file->s);
-#endif
+ BLI_stat(fullname, &file->s);
file->type = file->s.st_mode;
file->flags = 0;
dir_ctx->nrfiles++;
@@ -473,11 +458,7 @@ size_t BLI_file_size(const char *path)
int BLI_exists(const char *name)
{
#if defined(WIN32)
-#ifndef __MINGW32__
- struct _stat64 st;
-#else
- struct _stati64 st;
-#endif
+ BLI_stat_t st;
wchar_t *tmp_16 = alloc_utf16_from_8(name, 1);
int len, res;
unsigned int old_error_mode;
@@ -506,11 +487,7 @@ int BLI_exists(const char *name)
* when looking for a file on an empty CD/DVD drive */
old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
-#ifndef __MINGW32__
- res = _wstat64(tmp_16, &st);
-#else
- res = _wstati64(tmp_16, &st);
-#endif
+ res = BLI_wstat(tmp_16, &st);
SetErrorMode(old_error_mode);
@@ -530,16 +507,22 @@ int BLI_stat(const char *path, BLI_stat_t *buffer)
int r;
UTF16_ENCODE(path);
- /* workaround error in MinGW64 headers, normally, a wstat should work */
-#ifndef __MINGW64__
- r = _wstat64(path_16, buffer);
-#else
- r = _wstati64(path_16, buffer);
-#endif
+ r = BLI_wstat(path_16, buffer);
UTF16_UN_ENCODE(path);
return r;
}
+
+int BLI_wstat(const wchar_t *path, BLI_stat_t *buffer)
+{
+#if (defined(_MSC_VER) && (_MSC_VER >= 1500)) || defined(__MINGW64__)
+ return _wstat64(path, buffer);
+#elif defined(__MINGW32__)
+ return _wstati64(path, buffer);
+#else
+ return _wstat(path, buffer);
+#endif
+}
#else
int BLI_stat(const char *path, struct stat *buffer)
{
diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c
index 892bb16a543..4ab568ece42 100644
--- a/source/blender/blenlib/intern/string.c
+++ b/source/blender/blenlib/intern/string.c
@@ -295,11 +295,21 @@ char *BLI_str_quoted_substrN(const char *__restrict str, const char *__restrict
startMatch = strstr(str, prefix) + prefixLen + 1;
if (startMatch) {
/* get the end point (i.e. where the next occurance of " is after the starting point) */
- endMatch = strchr(startMatch, '"'); /* " NOTE: this comment here is just so that my text editor still shows the functions ok... */
-
- if (endMatch)
+
+ endMatch = startMatch;
+ while ((endMatch = strchr(endMatch, '"'))) {
+ if (LIKELY(*(endMatch - 1) != '\\')) {
+ break;
+ }
+ else {
+ endMatch++;
+ }
+ }
+
+ if (endMatch) {
/* return the slice indicated */
return BLI_strdupn(startMatch, (size_t)(endMatch - startMatch));
+ }
}
return BLI_strdupn("", 0);
}
@@ -629,3 +639,45 @@ int BLI_str_rstrip_float_zero(char *str, const char pad)
return totstrip;
}
+
+/**
+ * Return index of a string in a string array.
+ *
+ * \param str The string to find.
+ * \param str_array Array of strings.
+ * \param str_array_len The length of the array, or -1 for a NULL-terminated array.
+ * \return The index of str in str_array or -1.
+ */
+int BLI_str_index_in_array_n(const char *str, const char **str_array, const int str_array_len)
+{
+ int index;
+ const char **str_iter = str_array;
+
+ for (index = 0; index < str_array_len; str_iter++, index++) {
+ if (STREQ(str, *str_iter)) {
+ return index;
+ }
+ }
+ return -1;
+}
+
+/**
+ * Return index of a string in a string array.
+ *
+ * \param str The string to find.
+ * \param str_array Array of strings, (must be NULL-terminated).
+ * \return The index of str in str_array or -1.
+ */
+int BLI_str_index_in_array(const char *str, const char **str_array)
+{
+ int index;
+ const char **str_iter = str_array;
+
+ for (index = 0; *str_iter; str_iter++, index++) {
+ if (STREQ(str, *str_iter)) {
+ return index;
+ }
+ }
+ return -1;
+}
+
diff --git a/source/blender/blenlib/intern/winstuff_dir.c b/source/blender/blenlib/intern/winstuff_dir.c
index 90250de1683..3d669a869f9 100644
--- a/source/blender/blenlib/intern/winstuff_dir.c
+++ b/source/blender/blenlib/intern/winstuff_dir.c
@@ -28,7 +28,7 @@
* (opendir, readdir, closedir)
*/
-#if defined(WIN32) && !defined(FREE_WINDOWS)
+#ifdef WIN32
/* standalone for inclusion in binaries other then blender */
# ifdef USE_STANDALONE
@@ -44,6 +44,13 @@
#include "BLI_utildefines.h"
#include "utfconv.h"
+/* Note: MinGW (FREE_WINDOWS) has opendir() and _wopendir(), and only the
+* latter accepts a path name of wchar_t type. Rather than messing up with
+* extra #ifdef's here and there, Blender's own implementations of opendir()
+* and related functions are used to properly support paths with non-ASCII
+* characters. (kjym3)
+*/
+
DIR *opendir(const char *path)
{
wchar_t *path_16 = alloc_utf16_from_8(path, 0);
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 066c4ea67f1..91753158d02 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -4450,6 +4450,9 @@ static void lib_link_object(FileData *fd, Main *main)
steeringa->target = newlibadr(fd, ob->id.lib, steeringa->target);
steeringa->navmesh = newlibadr(fd, ob->id.lib, steeringa->navmesh);
}
+ else if(act->type == ACT_MOUSE) {
+ /* bMouseActuator *moa= act->data; */
+ }
}
{
@@ -8924,7 +8927,7 @@ static ID *append_named_part_ex(const bContext *C, Main *mainl, FileData *fd, co
ob->lay = v3d ? v3d->layact : scene->lay;
}
- ob->mode = 0;
+ ob->mode = OB_MODE_OBJECT;
base->lay = ob->lay;
base->object = ob;
ob->id.us++;
diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c
index cf74ef068eb..3890e07bb65 100644
--- a/source/blender/blenloader/intern/versioning_260.c
+++ b/source/blender/blenloader/intern/versioning_260.c
@@ -829,7 +829,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *main)
for (i = 0; i < 3; i++) {
if ( (ob->dsize[i] == 0.0f) || /* simple case, user never touched dsize */
(ob->size[i] == 0.0f)) /* cant scale the dsize to give a non zero result,
- so fallback to 1.0f */
+ * so fallback to 1.0f */
{
ob->dscale[i] = 1.0f;
}
diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c
index 00fcd2d7591..b812cf13d3b 100644
--- a/source/blender/blenloader/intern/versioning_270.c
+++ b/source/blender/blenloader/intern/versioning_270.c
@@ -251,49 +251,51 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
- if (!DNA_struct_elem_find(fd->filesdna, "Material", "int", "mode2")) {
- Material *ma;
+ if (!MAIN_VERSION_ATLEAST(main, 271, 0)) {
+ if (!DNA_struct_elem_find(fd->filesdna, "Material", "int", "mode2")) {
+ Material *ma;
- for (ma = main->mat.first; ma; ma = ma->id.next)
- ma->mode2 = MA_CASTSHADOW;
- }
-
- if (!DNA_struct_elem_find(fd->filesdna, "RenderData", "BakeData", "bake")) {
- Scene *sce;
+ for (ma = main->mat.first; ma; ma = ma->id.next)
+ ma->mode2 = MA_CASTSHADOW;
+ }
- for (sce = main->scene.first; sce; sce = sce->id.next) {
- sce->r.bake.flag = R_BAKE_CLEAR;
- sce->r.bake.width = 512;
- sce->r.bake.height = 512;
- sce->r.bake.margin = 16;
- sce->r.bake.normal_space = R_BAKE_SPACE_TANGENT;
- sce->r.bake.normal_swizzle[0] = R_BAKE_POSX;
- sce->r.bake.normal_swizzle[1] = R_BAKE_POSY;
- sce->r.bake.normal_swizzle[2] = R_BAKE_POSZ;
- BLI_strncpy(sce->r.bake.filepath, U.renderdir, sizeof(sce->r.bake.filepath));
-
- sce->r.bake.im_format.planes = R_IMF_PLANES_RGBA;
- sce->r.bake.im_format.imtype = R_IMF_IMTYPE_PNG;
- sce->r.bake.im_format.depth = R_IMF_CHAN_DEPTH_8;
- sce->r.bake.im_format.quality = 90;
- sce->r.bake.im_format.compress = 15;
+ if (!DNA_struct_elem_find(fd->filesdna, "RenderData", "BakeData", "bake")) {
+ Scene *sce;
+
+ for (sce = main->scene.first; sce; sce = sce->id.next) {
+ sce->r.bake.flag = R_BAKE_CLEAR;
+ sce->r.bake.width = 512;
+ sce->r.bake.height = 512;
+ sce->r.bake.margin = 16;
+ sce->r.bake.normal_space = R_BAKE_SPACE_TANGENT;
+ sce->r.bake.normal_swizzle[0] = R_BAKE_POSX;
+ sce->r.bake.normal_swizzle[1] = R_BAKE_POSY;
+ sce->r.bake.normal_swizzle[2] = R_BAKE_POSZ;
+ BLI_strncpy(sce->r.bake.filepath, U.renderdir, sizeof(sce->r.bake.filepath));
+
+ sce->r.bake.im_format.planes = R_IMF_PLANES_RGBA;
+ sce->r.bake.im_format.imtype = R_IMF_IMTYPE_PNG;
+ sce->r.bake.im_format.depth = R_IMF_CHAN_DEPTH_8;
+ sce->r.bake.im_format.quality = 90;
+ sce->r.bake.im_format.compress = 15;
+ }
}
- }
- if (!DNA_struct_elem_find(fd->filesdna, "FreestyleLineStyle", "float", "texstep")) {
- FreestyleLineStyle *linestyle;
+ if (!DNA_struct_elem_find(fd->filesdna, "FreestyleLineStyle", "float", "texstep")) {
+ FreestyleLineStyle *linestyle;
- for (linestyle = main->linestyle.first; linestyle; linestyle = linestyle->id.next) {
- linestyle->flag |= LS_TEXTURE;
- linestyle->texstep = 1.0;
+ for (linestyle = main->linestyle.first; linestyle; linestyle = linestyle->id.next) {
+ linestyle->flag |= LS_TEXTURE;
+ linestyle->texstep = 1.0;
+ }
}
- }
- {
- Scene *scene;
- for (scene = main->scene.first; scene; scene = scene->id.next) {
- int num_layers = BLI_countlist(&scene->r.layers);
- scene->r.actlay = min_ff(scene->r.actlay, num_layers - 1);
+ {
+ Scene *scene;
+ for (scene = main->scene.first; scene; scene = scene->id.next) {
+ int num_layers = BLI_countlist(&scene->r.layers);
+ scene->r.actlay = min_ff(scene->r.actlay, num_layers - 1);
+ }
}
}
}
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 45139789f1e..a0198a687a2 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -1237,6 +1237,9 @@ static void write_actuators(WriteData *wd, ListBase *lb)
case ACT_STEERING:
writestruct(wd, DATA, "bSteeringActuator", 1, act->data);
break;
+ case ACT_MOUSE:
+ writestruct(wd, DATA, "bMouseActuator", 1, act->data);
+ break;
default:
; /* error: don't know how to write this file */
}
diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c
index b1d41c9c5c6..812c223dcc4 100644
--- a/source/blender/bmesh/intern/bmesh_core.c
+++ b/source/blender/bmesh/intern/bmesh_core.c
@@ -32,6 +32,7 @@
#include "BLI_array.h"
#include "BLI_alloca.h"
#include "BLI_smallhash.h"
+#include "BLI_stackdefines.h"
#include "BLF_translation.h"
@@ -2113,8 +2114,6 @@ void bmesh_vert_separate(BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len
}
#endif
- STACK_FREE(stack);
-
BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
i = GET_INT_FROM_POINTER(BLI_smallhash_lookup(&visithash, (uintptr_t)e));
if (i == 0) {
diff --git a/source/blender/bmesh/intern/bmesh_edgeloop.c b/source/blender/bmesh/intern/bmesh_edgeloop.c
index c8a954ab6a3..f01e1197bb7 100644
--- a/source/blender/bmesh/intern/bmesh_edgeloop.c
+++ b/source/blender/bmesh/intern/bmesh_edgeloop.c
@@ -657,7 +657,7 @@ bool BM_edgeloop_calc_normal_aligned(BMesh *UNUSED(bm), BMEdgeLoopStore *el_stor
void BM_edgeloop_flip(BMesh *UNUSED(bm), BMEdgeLoopStore *el_store)
{
negate_v3(el_store->no);
- BLI_reverselist(&el_store->verts);
+ BLI_listbase_reverse(&el_store->verts);
}
void BM_edgeloop_expand(BMesh *UNUSED(bm), BMEdgeLoopStore *el_store, int el_store_len)
diff --git a/source/blender/bmesh/intern/bmesh_error.h b/source/blender/bmesh/intern/bmesh_error.h
index 0f23f87c143..6cd37011814 100644
--- a/source/blender/bmesh/intern/bmesh_error.h
+++ b/source/blender/bmesh/intern/bmesh_error.h
@@ -59,17 +59,20 @@ void BMO_error_clear(BMesh *bm);
/*------ error code defines -------*/
/*error messages*/
-#define BMERR_SELF_INTERSECTING 1
-#define BMERR_DISSOLVEDISK_FAILED 2
-#define BMERR_CONNECTVERT_FAILED 3
-#define BMERR_WALKER_FAILED 4
-#define BMERR_DISSOLVEFACES_FAILED 5
-#define BMERR_DISSOLVEVERTS_FAILED 6
-#define BMERR_TESSELLATION 7
-#define BMERR_NONMANIFOLD 8
-#define BMERR_INVALID_SELECTION 9
-#define BMERR_MESH_ERROR 10
-#define BMERR_CONVEX_HULL_FAILED 11
+enum {
+ BMERR_SELF_INTERSECTING = 1,
+ BMERR_DISSOLVEDISK_FAILED,
+ BMERR_CONNECTVERT_FAILED,
+ BMERR_WALKER_FAILED,
+ BMERR_DISSOLVEFACES_FAILED,
+ BMERR_TESSELLATION,
+ BMERR_NONMANIFOLD,
+ BMERR_INVALID_SELECTION,
+ BMERR_MESH_ERROR,
+ BMERR_CONVEX_HULL_FAILED,
+
+ BMERR_TOTAL,
+};
/* BMESH_ASSERT */
#ifdef WITH_ASSERT_ABORT
diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.c b/source/blender/bmesh/intern/bmesh_mesh_conv.c
index b7be0cc0ae2..05f3ff5b60b 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_conv.c
+++ b/source/blender/bmesh/intern/bmesh_mesh_conv.c
@@ -233,7 +233,7 @@ void BM_mesh_bm_from_me(BMesh *bm, Mesh *me,
BMEdge *e, **etable = NULL;
BMFace *f;
float (*keyco)[3] = NULL;
- int totuv, i, j;
+ int totuv, totloops, i, j;
int cd_vert_bweight_offset;
int cd_edge_bweight_offset;
@@ -395,7 +395,7 @@ void BM_mesh_bm_from_me(BMesh *bm, Mesh *me,
mloop = me->mloop;
mp = me->mpoly;
- for (i = 0; i < me->totpoly; i++, mp++) {
+ for (i = 0, totloops = 0; i < me->totpoly; i++, mp++) {
BMLoop *l_iter;
BMLoop *l_first;
@@ -426,6 +426,9 @@ void BM_mesh_bm_from_me(BMesh *bm, Mesh *me,
j = mp->loopstart;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
+ /* don't use 'j' since we may have skipped some faces, hence some loops. */
+ BM_elem_index_set(l_iter, totloops++); /* set_ok */
+
/* Save index of correspsonding MLoop */
CustomData_to_bmesh_block(&me->ldata, &bm->ldata, j++, &l_iter->head.data, true);
} while ((l_iter = l_iter->next) != l_first);
@@ -438,8 +441,7 @@ void BM_mesh_bm_from_me(BMesh *bm, Mesh *me,
}
}
- bm->elem_index_dirty &= ~BM_FACE; /* added in order, clear dirty flag */
- bm->elem_index_dirty |= BM_LOOP; /* did not set the loop indices */
+ bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP); /* added in order, clear dirty flag */
if (me->mselect && me->totselect != 0) {
diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c
index f8cca0a3707..7c77302c942 100644
--- a/source/blender/bmesh/intern/bmesh_opdefines.c
+++ b/source/blender/bmesh/intern/bmesh_opdefines.c
@@ -1904,4 +1904,4 @@ const BMOpDefine *bmo_opdefines[] = {
&bmo_wireframe_def,
};
-const int bmo_opdefines_total = (sizeof(bmo_opdefines) / sizeof(void *));
+const int bmo_opdefines_total = ARRAY_SIZE(bmo_opdefines);
diff --git a/source/blender/bmesh/intern/bmesh_operators.c b/source/blender/bmesh/intern/bmesh_operators.c
index dc419fab739..7f872613896 100644
--- a/source/blender/bmesh/intern/bmesh_operators.c
+++ b/source/blender/bmesh/intern/bmesh_operators.c
@@ -54,13 +54,13 @@ static const char *bmo_error_messages[] = {
N_("Could not connect vertices"),
N_("Could not traverse mesh"),
N_("Could not dissolve faces"),
- N_("Could not dissolve vertices"),
N_("Tessellation error"),
N_("Cannot deal with non-manifold geometry"),
N_("Invalid selection"),
N_("Internal mesh error"),
};
+BLI_STATIC_ASSERT(ARRAY_SIZE(bmo_error_messages) + 1 == BMERR_TOTAL, "message mismatch");
/* operator slot type information - size of one element of the type given. */
const int BMO_OPSLOT_TYPEINFO[BMO_OP_SLOT_TOTAL_TYPES] = {
diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c
index 4065ba33ee1..307c391a406 100644
--- a/source/blender/bmesh/intern/bmesh_polygon.c
+++ b/source/blender/bmesh/intern/bmesh_polygon.c
@@ -191,7 +191,7 @@ void BM_face_calc_tessellation(const BMFace *f, BMLoop **r_loops, unsigned int (
} while ((l_iter = l_iter->next) != l_first);
/* complete the loop */
- BLI_polyfill_calc((const float (*)[2])projverts, f->len, r_index);
+ BLI_polyfill_calc((const float (*)[2])projverts, f->len, -1, r_index);
}
}
@@ -833,7 +833,7 @@ void BM_face_triangulate(BMesh *bm, BMFace *f,
mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co);
}
- BLI_polyfill_calc_arena((const float (*)[2])projverts, f->len, tris,
+ BLI_polyfill_calc_arena((const float (*)[2])projverts, f->len, -1, tris,
sf_arena);
if (use_beauty) {
@@ -1303,7 +1303,7 @@ void BM_bmesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptr
j++;
} while ((l_iter = l_iter->next) != l_first);
- BLI_polyfill_calc_arena((const float (*)[2])projverts, efa->len, tris, arena);
+ BLI_polyfill_calc_arena((const float (*)[2])projverts, efa->len, -1, tris, arena);
for (j = 0; j < totfilltri; j++) {
BMLoop **l_ptr = looptris[i++];
diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c
index 61e1a15c605..d35625566c0 100644
--- a/source/blender/bmesh/intern/bmesh_queries.c
+++ b/source/blender/bmesh/intern/bmesh_queries.c
@@ -36,6 +36,7 @@
#include "BLI_math.h"
#include "BLI_alloca.h"
#include "BLI_linklist.h"
+#include "BLI_stackdefines.h"
#include "bmesh.h"
#include "intern/bmesh_private.h"
diff --git a/source/blender/bmesh/intern/bmesh_queries_inline.h b/source/blender/bmesh/intern/bmesh_queries_inline.h
index a2a0a15faad..0856b9846c1 100644
--- a/source/blender/bmesh/intern/bmesh_queries_inline.h
+++ b/source/blender/bmesh/intern/bmesh_queries_inline.h
@@ -137,4 +137,17 @@ BLI_INLINE bool BM_loop_is_adjacent(const BMLoop *l_a, const BMLoop *l_b)
return (ELEM(l_b, l_a->next, l_a->prev));
}
+/**
+ * Check if we have a single wire edge user.
+ */
+BLI_INLINE bool BM_vert_is_wire_endpoint(const BMVert *v)
+{
+ const BMEdge *e = v->e;
+ if (e && e->l == NULL) {
+ const BMDiskLink *dl = (e->v1 == v) ? &e->v1_disk_link : &e->v2_disk_link;
+ return (dl->next == e);
+ }
+ return false;
+}
+
#endif /* __BMESH_QUERIES_INLINE_H__ */
diff --git a/source/blender/bmesh/intern/bmesh_walkers_impl.c b/source/blender/bmesh/intern/bmesh_walkers_impl.c
index 8ca254a94f1..406dd412d6d 100644
--- a/source/blender/bmesh/intern/bmesh_walkers_impl.c
+++ b/source/blender/bmesh/intern/bmesh_walkers_impl.c
@@ -919,8 +919,8 @@ static void *bmw_FaceLoopWalker_step(BMWalker *walker)
}
/* both may already exist */
- BLI_gset_reinsert(walker->visit_set_alt, l->e, NULL);
- BLI_gset_reinsert(walker->visit_set, l->f, NULL);
+ BLI_gset_add(walker->visit_set_alt, l->e);
+ BLI_gset_add(walker->visit_set, l->f);
}
return f;
@@ -1352,4 +1352,4 @@ BMWalker *bm_walker_types[] = {
&bmw_ConnectedVertexWalker_Type, /* BMW_CONNECTED_VERTEX */
};
-const int bm_totwalkers = sizeof(bm_walker_types) / sizeof(*bm_walker_types);
+const int bm_totwalkers = ARRAY_SIZE(bm_walker_types);
diff --git a/source/blender/bmesh/operators/bmo_bisect_plane.c b/source/blender/bmesh/operators/bmo_bisect_plane.c
index 74cb9d67e88..378cf405e16 100644
--- a/source/blender/bmesh/operators/bmo_bisect_plane.c
+++ b/source/blender/bmesh/operators/bmo_bisect_plane.c
@@ -29,6 +29,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
+#include "BLI_stackdefines.h"
#include "BLI_math.h"
#include "bmesh.h"
@@ -103,7 +104,6 @@ void bmo_bisect_plane_exec(BMesh *bm, BMOperator *op)
BM_vert_kill(bm, v);
}
- STACK_FREE(vert_arr);
MEM_freeN(vert_arr);
}
diff --git a/source/blender/bmesh/operators/bmo_connect.c b/source/blender/bmesh/operators/bmo_connect.c
index 5a00401b3c7..8b0033a9342 100644
--- a/source/blender/bmesh/operators/bmo_connect.c
+++ b/source/blender/bmesh/operators/bmo_connect.c
@@ -27,6 +27,7 @@
*/
#include "BLI_utildefines.h"
+#include "BLI_stackdefines.h"
#include "BLI_alloca.h"
#include "BLI_linklist_stack.h"
diff --git a/source/blender/bmesh/operators/bmo_dissolve.c b/source/blender/bmesh/operators/bmo_dissolve.c
index 334242f3100..36a936c6622 100644
--- a/source/blender/bmesh/operators/bmo_dissolve.c
+++ b/source/blender/bmesh/operators/bmo_dissolve.c
@@ -211,9 +211,9 @@ void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op)
if (use_verts) {
BMIter viter;
- BMVert *v;
+ BMVert *v, *v_next;
- BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
+ BM_ITER_MESH_MUTABLE (v, v_next, &viter, bm, BM_VERTS_OF_MESH) {
if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
if (BM_vert_edge_count(v) == 2) {
BM_vert_collapse_edge(bm, v->e, v, true);
@@ -335,126 +335,85 @@ void bmo_dissolve_edges_exec(BMesh *bm, BMOperator *op)
}
}
-static bool test_extra_verts(BMesh *bm, BMVert *v)
-{
- BMIter fiter, liter, eiter, fiter_sub;
- BMFace *f;
- BMLoop *l;
- BMEdge *e;
-
- /* test faces around verts for verts that would be wrongly killed
- * by dissolve faces. */
- BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) {
- BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
- if (!BMO_elem_flag_test(bm, l->v, VERT_MARK)) {
- /* if an edge around a vert is a boundary edge,
- * then dissolve faces won't destroy it.
- * also if it forms a boundary with one
- * of the face region */
- bool found = false;
- BM_ITER_ELEM (e, &eiter, l->v, BM_EDGES_OF_VERT) {
- BMFace *f_iter;
- if (BM_edge_is_boundary(e)) {
- found = true;
- }
- else {
- BM_ITER_ELEM (f_iter, &fiter_sub, e, BM_FACES_OF_EDGE) {
- if (!BMO_elem_flag_test(bm, f_iter, FACE_MARK)) {
- found = true;
- break;
- }
- }
- }
- if (found == true) {
- break;
- }
- }
- if (found == false) {
- return false;
- }
- }
- }
- }
-
- return true;
-}
void bmo_dissolve_verts_exec(BMesh *bm, BMOperator *op)
{
- BMIter iter, fiter;
+ BMOIter oiter;
+ BMIter iter;
BMVert *v, *v_next;
- BMFace *f;
+ BMEdge *e, *e_next;
+ BMFace *act_face = bm->act_face;
const bool use_face_split = BMO_slot_bool_get(op->slots_in, "use_face_split");
+ BMO_ITER (v, &oiter, op->slots_in, "verts", BM_VERT) {
+ BMIter itersub;
+
+ BMO_elem_flag_enable(bm, v, VERT_MARK | VERT_ISGC);
+
+ BM_ITER_ELEM (e, &itersub, v, BM_EDGES_OF_VERT) {
+ BMO_elem_flag_enable(bm, e, EDGE_ISGC);
+ }
+ }
- BMO_slot_buffer_flag_enable(bm, op->slots_in, "verts", BM_VERT, VERT_MARK);
-
if (use_face_split) {
bm_face_split(bm, VERT_MARK);
}
- BM_ITER_MESH_MUTABLE (v, v_next, &iter, bm, BM_VERTS_OF_MESH) {
- if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
- /* check if it's a two-valence ver */
- if (BM_vert_edge_count(v) == 2) {
+ BMO_ITER (v, &oiter, op->slots_in, "verts", BM_VERT) {
+ BMIter itersub;
+ BMLoop *l_first;
+ BM_ITER_ELEM (l_first, &itersub, v, BM_LOOPS_OF_VERT) {
+ BMLoop *l_iter;
+ l_iter = l_first;
+ do {
+ BMO_elem_flag_enable(bm, l_iter->v, VERT_ISGC);
+ BMO_elem_flag_enable(bm, l_iter->e, EDGE_ISGC);
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
- /* collapse the ver */
- /* previously the faces were joined, but collapsing between 2 edges
- * gives some advantage/difference in using vertex-dissolve over edge-dissolve */
-#if 0
- BM_vert_collapse_faces(bm, v->e, v, 1.0f, true, true);
-#else
- BM_vert_collapse_edge(bm, v->e, v, true);
-#endif
+ BMO_ITER (v, &oiter, op->slots_in, "verts", BM_VERT) {
+ BMIter itersub;
- continue;
- }
+ if (BM_vert_edge_count(v) != 2) {
+ BM_ITER_ELEM (e, &itersub, v, BM_EDGES_OF_VERT) {
+ BMFace *fa, *fb;
+ if (BM_edge_face_pair(e, &fa, &fb)) {
+ BMFace *f_new;
- BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) {
- BMO_elem_flag_enable(bm, f, FACE_MARK | FACE_ORIG);
- }
-
- /* check if our additions to the input to face dissolve
- * will destroy nonmarked vertices. */
- if (!test_extra_verts(bm, v)) {
- BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) {
- if (BMO_elem_flag_test(bm, f, FACE_ORIG)) {
- BMO_elem_flag_disable(bm, f, FACE_MARK | FACE_ORIG);
+ /* join faces */
+ f_new = BM_faces_join_pair(bm, fa, fb, e, false);
+
+ /* maintain active face */
+ if (act_face && bm->act_face == NULL) {
+ bm->act_face = f_new;
}
}
}
- else {
- BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) {
- BMO_elem_flag_disable(bm, f, FACE_ORIG);
- }
- }
}
}
- BMO_op_callf(bm, op->flag, "dissolve_faces faces=%ff", FACE_MARK);
- if (BMO_error_occurred(bm)) {
- const char *msg;
-
- BMO_error_get(bm, &msg, NULL);
- BMO_error_clear(bm);
- BMO_error_raise(bm, op, BMERR_DISSOLVEVERTS_FAILED, msg);
+ /* Cleanup geometry (#BM_faces_join_pair, but it removes geometry we're looping on)
+ * so do this in a separate pass instead. */
+ BM_ITER_MESH_MUTABLE (e, e_next, &iter, bm, BM_EDGES_OF_MESH) {
+ if ((e->l == NULL) && BMO_elem_flag_test(bm, e, EDGE_ISGC)) {
+ BM_edge_kill(bm, e);
+ }
}
-
- /* clean up any remaining */
- /* note: don't use BM_ITER_MESH_MUTABLE here, even though vertices are removed (T37559) */
- BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ BM_ITER_MESH_MUTABLE (v, v_next, &iter, bm, BM_VERTS_OF_MESH) {
+ if ((v->e == NULL) && BMO_elem_flag_test(bm, v, VERT_ISGC)) {
+ BM_vert_kill(bm, v);
+ }
+ }
+ /* done with cleanup */
+
+ BM_ITER_MESH_MUTABLE (v, v_next, &iter, bm, BM_VERTS_OF_MESH) {
if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
- if (!BM_vert_dissolve(bm, v)) {
- BMO_error_raise(bm, op, BMERR_DISSOLVEVERTS_FAILED, NULL);
- return;
+ if (BM_vert_edge_count(v) == 2) {
+ BM_vert_collapse_edge(bm, v->e, v, true);
}
-#ifdef DEBUG
- /* workaround debug assert */
- iter.count = bm->totvert;
-#endif
}
}
-
}
/* Limited Dissolve */
diff --git a/source/blender/bmesh/operators/bmo_extrude.c b/source/blender/bmesh/operators/bmo_extrude.c
index f924d478f1a..510c3ae0078 100644
--- a/source/blender/bmesh/operators/bmo_extrude.c
+++ b/source/blender/bmesh/operators/bmo_extrude.c
@@ -212,13 +212,20 @@ void bmo_extrude_vert_indiv_exec(BMesh *bm, BMOperator *op)
for (v = BMO_iter_new(&siter, op->slots_in, "verts", BM_VERT); v; v = BMO_iter_step(&siter)) {
dupev = BM_vert_create(bm, v->co, v, BM_CREATE_NOP);
+ BMO_elem_flag_enable(bm, dupev, EXT_KEEP);
+
if (has_vskin)
bm_extrude_disable_skin_root(bm, v);
- e = BM_edge_create(bm, v, dupev, NULL, BM_CREATE_NOP);
+ /* not essential, but ensures face normals from extruded edges are contiguous */
+ if (BM_vert_is_wire_endpoint(v)) {
+ if (v->e->v1 == v) {
+ SWAP(BMVert *, v, dupev);
+ }
+ }
+ e = BM_edge_create(bm, v, dupev, NULL, BM_CREATE_NOP);
BMO_elem_flag_enable(bm, e, EXT_KEEP);
- BMO_elem_flag_enable(bm, dupev, EXT_KEEP);
}
BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "verts.out", BM_VERT, EXT_KEEP);
@@ -408,7 +415,15 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op)
/* link isolated vert */
for (v = BMO_iter_new(&siter, dupeop.slots_out, "isovert_map.out", 0); v; v = BMO_iter_step(&siter)) {
BMVert *v2 = BMO_iter_map_value_ptr(&siter);
- BM_edge_create(bm, v, v2, v->e, BM_CREATE_NO_DOUBLE);
+
+ /* not essential, but ensures face normals from extruded edges are contiguous */
+ if (BM_vert_is_wire_endpoint(v)) {
+ if (v->e->v1 == v) {
+ SWAP(BMVert *, v, v2);
+ }
+ }
+
+ BM_edge_create(bm, v, v2, NULL, BM_CREATE_NO_DOUBLE);
}
/* cleanup */
diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c
index e837beba2c2..a17ab2133c1 100644
--- a/source/blender/bmesh/operators/bmo_removedoubles.c
+++ b/source/blender/bmesh/operators/bmo_removedoubles.c
@@ -31,6 +31,7 @@
#include "BLI_math.h"
#include "BLI_array.h"
#include "BLI_alloca.h"
+#include "BLI_stackdefines.h"
#include "BKE_customdata.h"
@@ -151,9 +152,6 @@ static void remdoubles_createface(BMesh *bm, BMFace *f, BMOpSlot *slot_targetmap
}
}
}
-
- STACK_FREE(edges);
- STACK_FREE(loops);
}
/**
diff --git a/source/blender/bmesh/operators/bmo_subdivide.c b/source/blender/bmesh/operators/bmo_subdivide.c
index 0730c287eaa..fc507cdbd21 100644
--- a/source/blender/bmesh/operators/bmo_subdivide.c
+++ b/source/blender/bmesh/operators/bmo_subdivide.c
@@ -165,14 +165,11 @@ static BMEdge *connect_smallest_face(BMesh *bm, BMVert *v_a, BMVert *v_b, BMFace
static void alter_co(BMVert *v, BMEdge *UNUSED(origed), const SubDParams *params, float perc,
BMVert *vsta, BMVert *vend)
{
- float tvec[3], prev_co[3], fac;
+ float tvec[3], fac;
float *co = BM_ELEM_CD_GET_VOID_P(v, params->shape_info.cd_vert_shape_offset_tmp);
int i;
-
- BM_vert_normal_update_all(v);
copy_v3_v3(co, v->co);
- copy_v3_v3(prev_co, co);
if (UNLIKELY(params->use_sphere)) { /* subdivide sphere */
normalize_v3(co);
@@ -236,7 +233,7 @@ static void alter_co(BMVert *v, BMEdge *UNUSED(origed), const SubDParams *params
* this by getting the normals and coords for each shape key and
* re-calculate the smooth value for each but this is quite involved.
* for now its ok to simply apply the difference IMHO - campbell */
- sub_v3_v3v3(tvec, prev_co, co);
+ sub_v3_v3v3(tvec, v->co, co);
if (params->shape_info.totlayer > 1) {
/* skip the last layer since its the temp */
@@ -755,7 +752,7 @@ static const SubDPattern *patterns[] = {
NULL,
};
-#define PATTERNS_TOT (sizeof(patterns) / sizeof(void *))
+#define PATTERNS_TOT ARRAY_SIZE(patterns)
typedef struct SubDFaceData {
BMVert *start;
diff --git a/source/blender/bmesh/operators/bmo_subdivide_edgering.c b/source/blender/bmesh/operators/bmo_subdivide_edgering.c
index 182bf0518ef..ae1ff5a7389 100644
--- a/source/blender/bmesh/operators/bmo_subdivide_edgering.c
+++ b/source/blender/bmesh/operators/bmo_subdivide_edgering.c
@@ -40,6 +40,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
+#include "BLI_stackdefines.h"
#include "BLI_alloca.h"
#include "BLI_math.h"
#include "BLI_listbase.h"
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index 78bbdd1b5d2..c583464ab6d 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -52,6 +52,7 @@
#define BEVEL_EPSILON_D 1e-6
#define BEVEL_EPSILON 1e-6f
#define BEVEL_EPSILON_SQ 1e-12f
+#define BEVEL_EPSILON_BIG 1e-4f
/* happens far too often, uncomment for development */
// #define BEVEL_ASSERT_PROJECT
@@ -554,13 +555,22 @@ static void slide_dist(EdgeHalf *e, BMVert *v, float d, float slideco[3])
madd_v3_v3fl(slideco, dir, -d);
}
-/* Is co not on the edge e? */
-static bool is_outside_edge(EdgeHalf *e, const float co[3])
+/* Is co not on the edge e? if not, return the closer end of e in ret_closer_v */
+static bool is_outside_edge(EdgeHalf *e, const float co[3], BMVert **ret_closer_v)
{
float d_squared;
d_squared = dist_squared_to_line_segment_v3(co, e->e->v1->co, e->e->v2->co);
- return d_squared > 10000.0f * BEVEL_EPSILON_SQ;
+ if (d_squared > BEVEL_EPSILON_BIG * BEVEL_EPSILON_BIG) {
+ if (len_squared_v3v3(co, e->e->v1->co) > len_squared_v3v3(co, e->e->v2->co))
+ *ret_closer_v = e->e->v2;
+ else
+ *ret_closer_v = e->e->v1;
+ return true;
+ }
+ else {
+ return false;
+ }
}
/*
@@ -579,13 +589,14 @@ static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f, float
{
float dir1[3], dir2[3], norm_v[3], norm_perp1[3], norm_perp2[3],
off1a[3], off1b[3], off2a[3], off2b[3], isect2[3], ang, d;
+ BMVert *closer_v;
/* get direction vectors for two offset lines */
sub_v3_v3v3(dir1, v->co, BM_edge_other_vert(e1->e, v)->co);
sub_v3_v3v3(dir2, BM_edge_other_vert(e2->e, v)->co, v->co);
ang = angle_v3v3(dir1, dir2);
- if (ang < 100.0f * BEVEL_EPSILON) {
+ if (ang < BEVEL_EPSILON_BIG) {
/* special case: e1 and e2 are parallel; put offset point perp to both, from v.
* need to find a suitable plane.
* if offsets are different, we're out of luck:
@@ -606,7 +617,7 @@ static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f, float
e2->offset_l = d;
copy_v3_v3(meetco, off1a);
}
- else if (fabsf(ang - (float)M_PI) < 100.0f * BEVEL_EPSILON) {
+ else if (fabsf(ang - (float)M_PI) < BEVEL_EPSILON_BIG) {
/* special case e1 and e2 are antiparallel, so bevel is into
* a zero-area face. Just make the offset point on the
* common line, at offset distance from v. */
@@ -656,14 +667,15 @@ static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f, float
/* The lines intersect, but is it at a reasonable place?
* One problem to check: if one of the offsets is 0, then don't
* want an intersection that is outside that edge itself.
- * This can happen if angle between them is > 180 degrees. */
- if (e1->offset_r == 0.0f && is_outside_edge(e1, meetco)) {
- copy_v3_v3(meetco, v->co);
- e2->offset_l = 0.0f;
+ * This can happen if angle between them is > 180 degrees,
+ * or if the offset amount is > the edge length*/
+ if (e1->offset_r == 0.0f && is_outside_edge(e1, meetco, &closer_v)) {
+ copy_v3_v3(meetco, closer_v->co);
+ e2->offset_l = len_v3v3(meetco, v->co);
}
- if (e2->offset_l == 0.0f && is_outside_edge(e2, meetco)) {
- copy_v3_v3(meetco, v->co);
- e1->offset_r = 0.0f;
+ if (e2->offset_l == 0.0f && is_outside_edge(e2, meetco, &closer_v)) {
+ copy_v3_v3(meetco, closer_v->co);
+ e1->offset_r = len_v3v3(meetco, v->co);
}
}
}
@@ -811,11 +823,11 @@ static void offset_in_two_planes(BevelParams *bp, EdgeHalf *e1, EdgeHalf *e2, Ed
add_v3_v3v3(off2b, off2a, dir2);
ang = angle_v3v3(dir1, dir2);
- if (ang < 100.0f * BEVEL_EPSILON) {
+ if (ang < BEVEL_EPSILON_BIG) {
/* lines are parallel; put intersection on emid */
offset_on_edge_between(bp, e1, e2, emid, v, meetco);
}
- else if (fabsf(ang - (float)M_PI) < 100.0f * BEVEL_EPSILON) {
+ else if (fabsf(ang - (float)M_PI) < BEVEL_EPSILON_BIG) {
slide_dist(e2, v, e2->offset_l, meetco);
d = dist_to_line_v3(meetco, v->co, BM_edge_other_vert(e1->e, v)->co);
if (fabsf(d - e1->offset_r) > BEVEL_EPSILON)
@@ -892,7 +904,7 @@ static void set_profile_params(BevelParams *bp, BoundVert *bndv)
{
EdgeHalf *e;
Profile *pro;
- float co1[3], co2[3], co3[3], d1[3], d2[3];
+ float co1[3], co2[3], co3[3], d1[3], d2[3], d3[3], l;
bool do_linear_interp;
copy_v3_v3(co1, bndv->nv.co);
@@ -905,6 +917,7 @@ static void set_profile_params(BevelParams *bp, BoundVert *bndv)
pro->super_r = bp->pro_super_r;
/* projection direction is direction of the edge */
sub_v3_v3v3(pro->proj_dir, e->e->v1->co, e->e->v2->co);
+ normalize_v3(pro->proj_dir);
project_to_edge(e->e, co1, co2, pro->midco);
/* put arc endpoints on plane with normal proj_dir, containing midco */
add_v3_v3v3(co3, co1, pro->proj_dir);
@@ -920,12 +933,18 @@ static void set_profile_params(BevelParams *bp, BoundVert *bndv)
/* default plane to project onto is the one with triangle co1 - midco - co2 in it */
sub_v3_v3v3(d1, pro->midco, co1);
sub_v3_v3v3(d2, pro->midco, co2);
+ normalize_v3(d1);
+ normalize_v3(d2);
cross_v3_v3v3(pro->plane_no, d1, d2);
- if (normalize_v3(pro->plane_no) < BEVEL_EPSILON) {
- /* co1 - midco -co2 are collinear - project onto that plane */
- cross_v3_v3v3(co3, d1, pro->proj_dir);
- cross_v3_v3v3(pro->plane_no, d1, co3);
- if (normalize_v3(pro->plane_no) < BEVEL_EPSILON) {
+ l = normalize_v3(pro->plane_no);
+ if (l <= BEVEL_EPSILON_BIG) {
+ /* co1 - midco -co2 are collinear - project plane that contains that line
+ * and is perpendicular to the plane containing it and the beveled edge */
+ cross_v3_v3v3(d3, d1, pro->proj_dir);
+ normalize_v3(d3);
+ cross_v3_v3v3(pro->plane_no, d1, d3);
+ l = normalize_v3(pro->plane_no);
+ if (l <= BEVEL_EPSILON_BIG) {
/* whole profile is collinear with edge: just interpolate */
do_linear_interp = true;
}
@@ -958,9 +977,9 @@ static void move_profile_plane(BoundVert *bndv, EdgeHalf *e1, EdgeHalf *e2)
sub_v3_v3v3(d2, e2->e->v1->co, e2->e->v2->co);
cross_v3_v3v3(no, d1, d2);
cross_v3_v3v3(no2, d1, bndv->profile.proj_dir);
- if (normalize_v3(no) > BEVEL_EPSILON && normalize_v3(no2) > BEVEL_EPSILON) {
+ if (normalize_v3(no) > BEVEL_EPSILON_BIG && normalize_v3(no2) > BEVEL_EPSILON_BIG) {
dot = fabsf(dot_v3v3(no, no2));
- if (fabsf(dot - 1.0f) > BEVEL_EPSILON)
+ if (fabsf(dot - 1.0f) > BEVEL_EPSILON_BIG)
copy_v3_v3(bndv->profile.plane_no, no);
}
}
@@ -972,7 +991,7 @@ static void move_profile_plane(BoundVert *bndv, EdgeHalf *e1, EdgeHalf *e2)
* The original vertex should form a third point of the desired plane. */
static void move_weld_profile_planes(BevVert *bv, BoundVert *bndv1, BoundVert *bndv2)
{
- float d1[3], d2[3], no[3], no2[3], no3[3], dot1, dot2;
+ float d1[3], d2[3], no[3], no2[3], no3[3], dot1, dot2, l1, l2, l3;
/* only do this if projecting, and d1, d2, and proj_dir are not coplanar */
if (is_zero_v3(bndv1->profile.proj_dir) || is_zero_v3(bndv2->profile.proj_dir))
@@ -980,22 +999,20 @@ static void move_weld_profile_planes(BevVert *bv, BoundVert *bndv1, BoundVert *b
sub_v3_v3v3(d1, bv->v->co, bndv1->nv.co);
sub_v3_v3v3(d2, bv->v->co, bndv2->nv.co);
cross_v3_v3v3(no, d1, d2);
+ l1 = normalize_v3(no);
/* "no" is new normal projection plane, but don't move if
- * it is coplanar with one or the other of the projection dirs */
+ * it is coplanar with both of the projection dirs */
cross_v3_v3v3(no2, d1, bndv1->profile.proj_dir);
+ l2 = normalize_v3(no2);
cross_v3_v3v3(no3, d2, bndv2->profile.proj_dir);
- if (normalize_v3(no) > BEVEL_EPSILON &&
- normalize_v3(no2) > BEVEL_EPSILON &&
- normalize_v3(no3) > BEVEL_EPSILON)
- {
+ l3 = normalize_v3(no3);
+ if (l1 > BEVEL_EPSILON && (l2 > BEVEL_EPSILON || l3 > BEVEL_EPSILON)) {
dot1 = fabsf(dot_v3v3(no, no2));
dot2 = fabsf(dot_v3v3(no, no3));
- if (fabsf(dot1 - 1.0f) > BEVEL_EPSILON &&
- fabsf(dot2 - 1.0f) > BEVEL_EPSILON)
- {
+ if (fabsf(dot1 - 1.0f) > BEVEL_EPSILON)
copy_v3_v3(bndv1->profile.plane_no, no);
+ if (fabsf(dot2 - 1.0f) > BEVEL_EPSILON)
copy_v3_v3(bndv2->profile.plane_no, no);
- }
}
}
@@ -1633,7 +1650,7 @@ static void adjust_offsets(BevelParams *bp)
bv->visited = false;
}
- q = BLI_gsqueue_new((int)sizeof(BevVert *));
+ q = BLI_gsqueue_new(sizeof(BevVert *));
/* the following loop terminates because at least one node is visited each time */
for (;;) {
/* look for root of a connected component in search graph */
@@ -1702,7 +1719,7 @@ static BoundVert *pipe_test(BevVert *bv)
sub_v3_v3v3(dir3, BM_edge_other_vert(v3->ebev->e, bv->v)->co, bv->v->co);
normalize_v3(dir1);
normalize_v3(dir3);
- if (angle_v3v3(dir1, dir3) < 100.0f * BEVEL_EPSILON) {
+ if (angle_v3v3(dir1, dir3) < BEVEL_EPSILON_BIG) {
epipe = v1->ebev;
break;
}
diff --git a/source/blender/bmesh/tools/bmesh_bisect_plane.c b/source/blender/bmesh/tools/bmesh_bisect_plane.c
index fcf768bc116..0bd68e7a461 100644
--- a/source/blender/bmesh/tools/bmesh_bisect_plane.c
+++ b/source/blender/bmesh/tools/bmesh_bisect_plane.c
@@ -38,6 +38,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
+#include "BLI_stackdefines.h"
#include "BLI_alloca.h"
#include "BLI_linklist.h"
#include "BLI_linklist_stack.h"
@@ -275,9 +276,9 @@ static void bm_face_bisect_verts(BMesh *bm, BMFace *f, const float plane[4], con
}
}
-finally:
- STACK_FREE(vert_split_arr);
+finally:
+ (void)vert_split_arr;
}
/* -------------------------------------------------------------------- */
diff --git a/source/blender/bmesh/tools/bmesh_edgenet.c b/source/blender/bmesh/tools/bmesh_edgenet.c
index ddf43949258..a08aa6184b5 100644
--- a/source/blender/bmesh/tools/bmesh_edgenet.c
+++ b/source/blender/bmesh/tools/bmesh_edgenet.c
@@ -203,12 +203,18 @@ static BMEdge *bm_edgenet_path_step(
BMVert *v_curr, LinkNode **v_ls,
VertNetInfo *vnet_info, BLI_mempool *path_pool)
{
- const VertNetInfo *vn_curr = &vnet_info[BM_elem_index_get(v_curr)];
+ const VertNetInfo *vn_curr;
BMEdge *e;
BMIter iter;
- unsigned int tot = 0;
- unsigned int v_ls_tot = 0;
+ unsigned int tot;
+ unsigned int v_ls_tot;
+
+
+begin:
+ tot = 0;
+ v_ls_tot = 0;
+ vn_curr = &vnet_info[BM_elem_index_get(v_curr)];
BM_ITER_ELEM (e, &iter, v_curr, BM_EDGES_OF_VERT) {
BMVert *v_next = BM_edge_other_vert(e, v_curr);
@@ -256,7 +262,12 @@ static BMEdge *bm_edgenet_path_step(
/* trick to walk along wire-edge paths */
if (v_ls_tot == 1 && tot == 1) {
v_curr = BLI_linklist_pop_pool(v_ls, path_pool);
+ /* avoid recursion, can crash on very large nets */
+#if 0
bm_edgenet_path_step(v_curr, v_ls, vnet_info, path_pool);
+#else
+ goto begin;
+#endif
}
return NULL;
diff --git a/source/blender/compositor/intern/COM_Debug.cpp b/source/blender/compositor/intern/COM_Debug.cpp
index a453af5ad13..470f8fd2ef7 100644
--- a/source/blender/compositor/intern/COM_Debug.cpp
+++ b/source/blender/compositor/intern/COM_Debug.cpp
@@ -398,7 +398,7 @@ void DebugInfo::graphviz(const ExecutionSystem *system)
char filename[FILE_MAX];
BLI_snprintf(basename, sizeof(basename), "compositor_%d.dot", m_file_index);
- BLI_join_dirfile(filename, sizeof(filename), BLI_temporary_dir(), basename);
+ BLI_join_dirfile(filename, sizeof(filename), BLI_temp_dir_session(), basename);
++m_file_index;
FILE *fp = BLI_fopen(filename, "wb");
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cpp b/source/blender/compositor/intern/COM_MemoryBuffer.cpp
index 04828bfe3f8..c1916f4a68f 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.cpp
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.cpp
@@ -46,7 +46,7 @@ MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, unsigned int chunkNumber, r
BLI_rcti_init(&this->m_rect, rect->xmin, rect->xmax, rect->ymin, rect->ymax);
this->m_memoryProxy = memoryProxy;
this->m_chunkNumber = chunkNumber;
- this->m_buffer = (float *)MEM_mallocN(sizeof(float) * determineBufferSize() * COM_NUMBER_OF_CHANNELS, "COM_MemoryBuffer");
+ this->m_buffer = (float *)MEM_mallocN_aligned(sizeof(float) * determineBufferSize() * COM_NUMBER_OF_CHANNELS, 16, "COM_MemoryBuffer");
this->m_state = COM_MB_ALLOCATED;
this->m_datatype = COM_DT_COLOR;
this->m_chunkWidth = this->m_rect.xmax - this->m_rect.xmin;
@@ -57,7 +57,7 @@ MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, rcti *rect)
BLI_rcti_init(&this->m_rect, rect->xmin, rect->xmax, rect->ymin, rect->ymax);
this->m_memoryProxy = memoryProxy;
this->m_chunkNumber = -1;
- this->m_buffer = (float *)MEM_mallocN(sizeof(float) * determineBufferSize() * COM_NUMBER_OF_CHANNELS, "COM_MemoryBuffer");
+ this->m_buffer = (float *)MEM_mallocN_aligned(sizeof(float) * determineBufferSize() * COM_NUMBER_OF_CHANNELS, 16, "COM_MemoryBuffer");
this->m_state = COM_MB_TEMPORARILY;
this->m_datatype = COM_DT_COLOR;
this->m_chunkWidth = this->m_rect.xmax - this->m_rect.xmin;
diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.cpp b/source/blender/compositor/operations/COM_BlurBaseOperation.cpp
index e7af9319f88..d5aafc7c2ae 100644
--- a/source/blender/compositor/operations/COM_BlurBaseOperation.cpp
+++ b/source/blender/compositor/operations/COM_BlurBaseOperation.cpp
@@ -91,6 +91,18 @@ float *BlurBaseOperation::make_gausstab(float rad, int size)
return gausstab;
}
+#ifdef __SSE2__
+__m128 *BlurBaseOperation::convert_gausstab_sse(const float *gausstab, float rad, int size)
+{
+ int n = 2 * size + 1;
+ __m128 *gausstab_sse = (__m128 *) MEM_mallocN_aligned(sizeof(__m128) * n, 16, "gausstab sse");
+ for (int i = 0; i < n; ++i) {
+ gausstab_sse[i] = _mm_set1_ps(gausstab[i]);
+ }
+ return gausstab_sse;
+}
+#endif
+
/* normalized distance from the current (inverted so 1.0 is close and 0.0 is far)
* 'ease' is applied after, looks nicer */
float *BlurBaseOperation::make_dist_fac_inverse(float rad, int size, int falloff)
diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.h b/source/blender/compositor/operations/COM_BlurBaseOperation.h
index 052a525ef2c..e97dd4d766d 100644
--- a/source/blender/compositor/operations/COM_BlurBaseOperation.h
+++ b/source/blender/compositor/operations/COM_BlurBaseOperation.h
@@ -27,6 +27,10 @@
#define MAX_GAUSSTAB_RADIUS 30000
+#ifdef __SSE2__
+# include <emmintrin.h>
+#endif
+
class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
private:
@@ -34,6 +38,9 @@ protected:
BlurBaseOperation(DataType data_type);
float *make_gausstab(float rad, int size);
+#ifdef __SSE2__
+ __m128 *convert_gausstab_sse(const float *gaustab, float rad, int size);
+#endif
float *make_dist_fac_inverse(float rad, int size, int falloff);
void updateSize();
diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cpp b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cpp
index d08924ca4ef..0aefba3bb7c 100644
--- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cpp
+++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cpp
@@ -31,6 +31,9 @@ extern "C" {
GaussianXBlurOperation::GaussianXBlurOperation() : BlurBaseOperation(COM_DT_COLOR)
{
this->m_gausstab = NULL;
+#ifdef __SSE2__
+ this->m_gausstab_sse = NULL;
+#endif
this->m_filtersize = 0;
}
@@ -54,8 +57,14 @@ void GaussianXBlurOperation::initExecution()
if (this->m_sizeavailable) {
float rad = max_ff(m_size * m_data.sizex, 0.0f);
m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS);
-
+
+ /* TODO(sergey): De-duplicate with the case below and Y blur. */
this->m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize);
+#ifdef __SSE2__
+ this->m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(this->m_gausstab,
+ rad,
+ m_filtersize);
+#endif
}
}
@@ -65,8 +74,13 @@ void GaussianXBlurOperation::updateGauss()
updateSize();
float rad = max_ff(m_size * m_data.sizex, 0.0f);
m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS);
-
+
this->m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize);
+#ifdef __SSE2__
+ this->m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(this->m_gausstab,
+ rad,
+ m_filtersize);
+#endif
}
}
@@ -88,12 +102,25 @@ void GaussianXBlurOperation::executePixel(float output[4], int x, int y, void *d
int step = getStep();
int offsetadd = getOffsetAdd();
int bufferindex = ((xmin - bufferstartx) * 4) + ((ymin - bufferstarty) * 4 * bufferwidth);
+
+#ifdef __SSE2__
+ __m128 accum_r = _mm_load_ps(color_accum);
+ for (int nx = xmin, index = (xmin - x) + this->m_filtersize; nx < xmax; nx += step, index += step) {
+ __m128 reg_a = _mm_load_ps(&buffer[bufferindex]);
+ reg_a = _mm_mul_ps(reg_a, this->m_gausstab_sse[index]);
+ accum_r = _mm_add_ps(accum_r, reg_a);
+ multiplier_accum += this->m_gausstab[index];
+ bufferindex += offsetadd;
+ }
+ _mm_store_ps(color_accum, accum_r);
+#else
for (int nx = xmin, index = (xmin - x) + this->m_filtersize; nx < xmax; nx += step, index += step) {
const float multiplier = this->m_gausstab[index];
madd_v4_v4fl(color_accum, &buffer[bufferindex], multiplier);
multiplier_accum += multiplier;
bufferindex += offsetadd;
}
+#endif
mul_v4_v4fl(output, color_accum, 1.0f / multiplier_accum);
}
@@ -105,6 +132,12 @@ void GaussianXBlurOperation::deinitExecution()
MEM_freeN(this->m_gausstab);
this->m_gausstab = NULL;
}
+#ifdef __SSE2__
+ if (this->m_gausstab_sse) {
+ MEM_freeN(this->m_gausstab_sse);
+ this->m_gausstab_sse = NULL;
+ }
+#endif
deinitMutex();
}
diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h
index 6442f214138..e391320a007 100644
--- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h
@@ -28,6 +28,9 @@
class GaussianXBlurOperation : public BlurBaseOperation {
private:
float *m_gausstab;
+#ifdef __SSE2__
+ __m128 *m_gausstab_sse;
+#endif
int m_filtersize;
void updateGauss();
public:
diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cpp b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cpp
index 8216b79372f..a05a1ab6a23 100644
--- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cpp
+++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cpp
@@ -31,6 +31,9 @@ extern "C" {
GaussianYBlurOperation::GaussianYBlurOperation() : BlurBaseOperation(COM_DT_COLOR)
{
this->m_gausstab = NULL;
+#ifdef __SSE2__
+ this->m_gausstab_sse = NULL;
+#endif
this->m_filtersize = 0;
}
@@ -54,8 +57,13 @@ void GaussianYBlurOperation::initExecution()
if (this->m_sizeavailable) {
float rad = max_ff(m_size * m_data.sizey, 0.0f);
m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS);
-
+
this->m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize);
+#ifdef __SSE2__
+ this->m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(this->m_gausstab,
+ rad,
+ m_filtersize);
+#endif
}
}
@@ -65,8 +73,13 @@ void GaussianYBlurOperation::updateGauss()
updateSize();
float rad = max_ff(m_size * m_data.sizey, 0.0f);
m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS);
-
+
this->m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize);
+#ifdef __SSE2__
+ this->m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(this->m_gausstab,
+ rad,
+ m_filtersize);
+#endif
}
}
@@ -88,6 +101,20 @@ void GaussianYBlurOperation::executePixel(float output[4], int x, int y, void *d
int index;
int step = getStep();
const int bufferIndexx = ((xmin - bufferstartx) * 4);
+
+#ifdef __SSE2__
+ __m128 accum_r = _mm_load_ps(color_accum);
+ for (int ny = ymin; ny < ymax; ny += step) {
+ index = (ny - y) + this->m_filtersize;
+ int bufferindex = bufferIndexx + ((ny - bufferstarty) * 4 * bufferwidth);
+ const float multiplier = this->m_gausstab[index];
+ __m128 reg_a = _mm_load_ps(&buffer[bufferindex]);
+ reg_a = _mm_mul_ps(reg_a, this->m_gausstab_sse[index]);
+ accum_r = _mm_add_ps(accum_r, reg_a);
+ multiplier_accum += multiplier;
+ }
+ _mm_store_ps(color_accum, accum_r);
+#else
for (int ny = ymin; ny < ymax; ny += step) {
index = (ny - y) + this->m_filtersize;
int bufferindex = bufferIndexx + ((ny - bufferstarty) * 4 * bufferwidth);
@@ -95,6 +122,7 @@ void GaussianYBlurOperation::executePixel(float output[4], int x, int y, void *d
madd_v4_v4fl(color_accum, &buffer[bufferindex], multiplier);
multiplier_accum += multiplier;
}
+#endif
mul_v4_v4fl(output, color_accum, 1.0f / multiplier_accum);
}
@@ -106,6 +134,12 @@ void GaussianYBlurOperation::deinitExecution()
MEM_freeN(this->m_gausstab);
this->m_gausstab = NULL;
}
+#ifdef __SSE2__
+ if (this->m_gausstab_sse) {
+ MEM_freeN(this->m_gausstab_sse);
+ this->m_gausstab_sse = NULL;
+ }
+#endif
deinitMutex();
}
diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h
index 16503360de2..22b6562077d 100644
--- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h
@@ -28,6 +28,9 @@
class GaussianYBlurOperation : public BlurBaseOperation {
private:
float *m_gausstab;
+#ifdef __SSE2__
+ __m128 *m_gausstab_sse;
+#endif
int m_filtersize;
void updateGauss();
public:
diff --git a/source/blender/datatoc/datatoc_icon.c b/source/blender/datatoc/datatoc_icon.c
index 59c9eeb3ed1..7df9269a4a8 100644
--- a/source/blender/datatoc/datatoc_icon.c
+++ b/source/blender/datatoc/datatoc_icon.c
@@ -314,6 +314,9 @@ static bool icon_merge(const char *file_src,
free(pixels);
+ /* only for bounds check */
+ (void)canvas_h;
+
return true;
}
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index 8e09aebff92..3530857266d 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -3228,7 +3228,7 @@ void ANIM_channel_debug_print_info(bAnimListElem *ale, short indent_level)
/* Check if some setting for a channel is enabled
* Returns: 1 = On, 0 = Off, -1 = Invalid
*/
-short ANIM_channel_setting_get(bAnimContext *ac, bAnimListElem *ale, int setting)
+short ANIM_channel_setting_get(bAnimContext *ac, bAnimListElem *ale, eAnimChannel_Settings setting)
{
bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
@@ -3301,7 +3301,7 @@ short ANIM_channel_setting_get(bAnimContext *ac, bAnimListElem *ale, int setting
* - setting: eAnimChannel_Settings
* - mode: eAnimChannels_SetFlag
*/
-void ANIM_channel_setting_set(bAnimContext *ac, bAnimListElem *ale, int setting, short mode)
+void ANIM_channel_setting_set(bAnimContext *ac, bAnimListElem *ale, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode)
{
bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index c92ab04ab84..0bc4a69aa06 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -74,7 +74,7 @@
/* Set the given animation-channel as the active one for the active context */
// TODO: extend for animdata types...
-void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int filter, void *channel_data, short channel_type)
+void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datatype, eAnimFilter_Flags filter, void *channel_data, eAnim_ChannelType channel_type)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
@@ -173,6 +173,8 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int f
case ANIMTYPE_DSLAT:
case ANIMTYPE_DSLINESTYLE:
case ANIMTYPE_DSSPK:
+ case ANIMTYPE_DSNTREE:
+ case ANIMTYPE_DSTEX:
{
/* need to verify that this data is valid for now */
if (ale && ale->adt) {
@@ -180,6 +182,17 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int f
}
break;
}
+
+ /* unhandled currently, but may be interesting */
+ case ANIMTYPE_GPLAYER:
+ case ANIMTYPE_MASKLAYER:
+ case ANIMTYPE_SHAPEKEY:
+ case ANIMTYPE_NLAACTION:
+ break;
+
+ /* other types */
+ default:
+ break;
}
}
@@ -193,7 +206,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int f
* - test: check if deselecting instead of selecting
* - sel: eAnimChannels_SetFlag;
*/
-void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, short test, short sel)
+void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types datatype, bool test, eAnimChannels_SetFlag sel)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
@@ -387,7 +400,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, s
* - setting: type of setting to set
* - on: whether the visibility setting has been enabled or disabled
*/
-void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, int setting, short mode)
+void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode)
{
bAnimListElem *ale, *match = NULL;
int prevLevel = 0, matchLevel = 0;
@@ -623,13 +636,13 @@ static int animedit_poll_channels_nla_tweakmode_off(bContext *C)
/* ****************** Rearrange Channels Operator ******************* */
/* constants for channel rearranging */
-/* WARNING: don't change exising ones without modifying rearrange func accordingly */
-enum {
+/* WARNING: don't change existing ones without modifying rearrange func accordingly */
+typedef enum eRearrangeAnimChan_Mode {
REARRANGE_ANIMCHAN_TOP = -2,
REARRANGE_ANIMCHAN_UP = -1,
REARRANGE_ANIMCHAN_DOWN = 1,
REARRANGE_ANIMCHAN_BOTTOM = 2
-};
+} eRearrangeAnimChan_Mode;
/* defines for rearranging channels */
static EnumPropertyItem prop_animchannel_rearrange_types[] = {
@@ -733,13 +746,13 @@ static bool rearrange_island_down(ListBase *list, tReorderChannelIsland *island)
/* push it down */
BLI_insertlinkafter(list, next, island);
- return 1;
+ return true;
}
}
/* else: no next channel, so we're at the bottom already, so can't move */
}
- return 0;
+ return false;
}
static bool rearrange_island_bottom(ListBase *list, tReorderChannelIsland *island)
@@ -761,10 +774,10 @@ static bool rearrange_island_bottom(ListBase *list, tReorderChannelIsland *islan
}
- return 1;
+ return true;
}
- return 0;
+ return false;
}
/* ............................. */
@@ -772,14 +785,14 @@ static bool rearrange_island_bottom(ListBase *list, tReorderChannelIsland *islan
/**
* typedef for channel rearranging function
*
- * \param list List that channels belong to
+ * \param list List of tReorderChannelIsland's that channels belong to
* \param island Island to be moved
* \return Whether operation was a success
*/
typedef bool (*AnimChanRearrangeFp)(ListBase *list, tReorderChannelIsland *island);
/* get rearranging function, given 'rearrange' mode */
-static AnimChanRearrangeFp rearrange_get_mode_func(short mode)
+static AnimChanRearrangeFp rearrange_get_mode_func(eRearrangeAnimChan_Mode mode)
{
switch (mode) {
case REARRANGE_ANIMCHAN_TOP:
@@ -798,7 +811,9 @@ static AnimChanRearrangeFp rearrange_get_mode_func(short mode)
/* Rearrange Islands Generics ------------------------------------- */
/* add channel into list of islands */
-static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *srcList, Link *channel, short type, const bool is_hidden)
+static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *srcList,
+ Link *channel, eAnim_ChannelType type,
+ const bool is_hidden)
{
tReorderChannelIsland *island = islands->last; /* always try to add to last island if possible */
bool is_sel = false, is_untouchable = false;
@@ -880,7 +895,8 @@ static void rearrange_animchannel_flatten_islands(ListBase *islands, ListBase *s
/* ............................. */
-static void rearrange_animchannels_filter_visible(ListBase *anim_data_visible, bAnimContext *ac, short type)
+/* get a list of all bAnimListElem's of a certain type which are currently visible */
+static void rearrange_animchannels_filter_visible(ListBase *anim_data_visible, bAnimContext *ac, eAnim_ChannelType type)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale, *ale_next;
@@ -904,7 +920,8 @@ static void rearrange_animchannels_filter_visible(ListBase *anim_data_visible, b
/* performing rearranging of channels using islands */
static bool rearrange_animchannel_islands(ListBase *list, AnimChanRearrangeFp rearrange_func,
- short mode, short type, ListBase *anim_data_visible)
+ eRearrangeAnimChan_Mode mode, eAnim_ChannelType type,
+ ListBase *anim_data_visible)
{
ListBase islands = {NULL, NULL};
Link *channel, *chanNext = NULL;
@@ -954,7 +971,7 @@ static bool rearrange_animchannel_islands(ListBase *list, AnimChanRearrangeFp re
* ! NLA tracks are displayed in opposite order, so directions need care
* mode: REARRANGE_ANIMCHAN_*
*/
-static void rearrange_nla_channels(bAnimContext *ac, AnimData *adt, short mode)
+static void rearrange_nla_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode)
{
AnimChanRearrangeFp rearrange_func;
ListBase anim_data_visible = {NULL, NULL};
@@ -982,7 +999,7 @@ static void rearrange_nla_channels(bAnimContext *ac, AnimData *adt, short mode)
/* Change the order drivers within AnimData block
* mode: REARRANGE_ANIMCHAN_*
*/
-static void rearrange_driver_channels(bAnimContext *ac, AnimData *adt, short mode)
+static void rearrange_driver_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode)
{
/* get rearranging function */
AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
@@ -1097,7 +1114,7 @@ static void join_groups_action_temp(bAction *act)
/* Change the order of anim-channels within action
* mode: REARRANGE_ANIMCHAN_*
*/
-static void rearrange_action_channels(bAnimContext *ac, bAction *act, short mode)
+static void rearrange_action_channels(bAnimContext *ac, bAction *act, eRearrangeAnimChan_Mode mode)
{
bActionGroup tgrp;
ListBase anim_data_visible = {NULL, NULL};
@@ -1764,7 +1781,7 @@ static EnumPropertyItem prop_animchannel_settings_types[] = {
* onlysel: only selected channels get the flag set
*/
// TODO: enable a setting which turns flushing on/off?
-static void setflag_anim_channels(bAnimContext *ac, short setting, short mode, short onlysel, short flush)
+static void setflag_anim_channels(bAnimContext *ac, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode, bool onlysel, bool flush)
{
ListBase anim_data = {NULL, NULL};
ListBase all_data = {NULL, NULL};
@@ -1837,8 +1854,9 @@ static void setflag_anim_channels(bAnimContext *ac, short setting, short mode, s
static int animchannels_setflag_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
- short mode, setting;
- short flush = 1;
+ eAnimChannel_Settings setting;
+ eAnimChannels_SetFlag mode;
+ bool flush = true;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0)
@@ -1850,12 +1868,12 @@ static int animchannels_setflag_exec(bContext *C, wmOperator *op)
/* check if setting is flushable */
if (setting == ACHANNEL_SETTING_EXPAND)
- flush = 0;
+ flush = false;
/* modify setting
* - only selected channels are affected
*/
- setflag_anim_channels(&ac, setting, mode, 1, flush);
+ setflag_anim_channels(&ac, setting, mode, true, flush);
/* send notifier that things have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
@@ -1968,7 +1986,7 @@ static void ANIM_OT_channels_editable_toggle(wmOperatorType *ot)
static int animchannels_expand_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
- short onlysel = 1;
+ bool onlysel = true;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0)
@@ -1976,10 +1994,10 @@ static int animchannels_expand_exec(bContext *C, wmOperator *op)
/* only affect selected channels? */
if (RNA_boolean_get(op->ptr, "all"))
- onlysel = 0;
+ onlysel = false;
/* modify setting */
- setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel, 0);
+ setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel, false);
/* send notifier that things have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
@@ -2010,7 +2028,7 @@ static void ANIM_OT_channels_expand(wmOperatorType *ot)
static int animchannels_collapse_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
- short onlysel = 1;
+ bool onlysel = true;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0)
@@ -2018,10 +2036,10 @@ static int animchannels_collapse_exec(bContext *C, wmOperator *op)
/* only affect selected channels? */
if (RNA_boolean_get(op->ptr, "all"))
- onlysel = 0;
+ onlysel = false;
/* modify setting */
- setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel, 0);
+ setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel, false);
/* send notifier that things have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
@@ -2044,7 +2062,7 @@ static void ANIM_OT_channels_collapse(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* props */
- ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All", "Collapse all channels (not just selected ones)");
+ ot->prop = RNA_def_boolean(ot->srna, "all", true, "All", "Collapse all channels (not just selected ones)");
}
/* ******************* Reenable Disabled Operator ******************* */
@@ -2052,7 +2070,7 @@ static void ANIM_OT_channels_collapse(wmOperatorType *ot)
static int animchannels_enable_poll(bContext *C)
{
ScrArea *sa = CTX_wm_area(C);
-
+
/* channels region test */
/* TODO: could enhance with actually testing if channels region? */
if (ELEM(NULL, sa, CTX_wm_region(C)))
@@ -2120,6 +2138,82 @@ static void ANIM_OT_channels_fcurves_enable(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/* ****************** Find / Set Filter Operator ******************** */
+
+/* XXX: make this generic? */
+static int animchannels_find_poll(bContext *C)
+{
+ ScrArea *sa = CTX_wm_area(C);
+
+ if (sa == NULL)
+ return 0;
+
+ /* animation editor with dopesheet */
+ return ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA);
+}
+
+/* find_invoke() - Get initial channels */
+static int animchannels_find_invoke(bContext *C, wmOperator *op, const wmEvent *evt)
+{
+ bAnimContext ac;
+
+ /* get editor data */
+ if (ANIM_animdata_get_context(C, &ac) == 0)
+ return OPERATOR_CANCELLED;
+
+ /* set initial filter text, and enable filter */
+ RNA_string_set(op->ptr, "query", ac.ads->searchstr);
+
+ /* defer to popup */
+ return WM_operator_props_popup(C, op, evt);
+}
+
+/* find_exec() - Called to set the value */
+static int animchannels_find_exec(bContext *C, wmOperator *op)
+{
+ bAnimContext ac;
+
+ /* get editor data */
+ if (ANIM_animdata_get_context(C, &ac) == 0)
+ return OPERATOR_CANCELLED;
+
+ /* update filter text, and ensure that filter is enabled if there's something there
+ * NOTE: we turn the filter off if there's nothing (this is a quicky shortcut for dismissing)
+ */
+ RNA_string_get(op->ptr, "query", ac.ads->searchstr);
+
+ if (ac.ads->searchstr[0]) {
+ ac.ads->filterflag |= ADS_FILTER_BY_FCU_NAME;
+ }
+ else {
+ ac.ads->filterflag &= ~ADS_FILTER_BY_FCU_NAME;
+ }
+
+ /* redraw */
+ WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static void ANIM_OT_channels_find(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Find Channels";
+ ot->idname = "ANIM_OT_channels_find";
+ ot->description = "Filter the set of channels shown to only include those with matching names";
+
+ /* callbacks */
+ ot->invoke = animchannels_find_invoke;
+ ot->exec = animchannels_find_exec;
+ ot->poll = animchannels_find_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_string(ot->srna, "query", "Query", sizeof(((bDopeSheet *)NULL)->searchstr), "", "Text to search for in channel names");
+}
+
/* ********************** Select All Operator *********************** */
static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
@@ -2132,9 +2226,9 @@ static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
/* 'standard' behavior - check if selected, then apply relevant selection */
if (RNA_boolean_get(op->ptr, "invert"))
- ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 0, ACHANNEL_SETFLAG_TOGGLE);
+ ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, false, ACHANNEL_SETFLAG_TOGGLE);
else
- ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_ADD);
+ ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, true, ACHANNEL_SETFLAG_ADD);
/* send notifier that things have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
@@ -2157,7 +2251,7 @@ static void ANIM_OT_channels_select_all_toggle(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* props */
- ot->prop = RNA_def_boolean(ot->srna, "invert", 0, "Invert", "");
+ ot->prop = RNA_def_boolean(ot->srna, "invert", false, "Invert", "");
}
/* ******************** Borderselect Operator *********************** */
@@ -2281,7 +2375,7 @@ static int animchannels_borderselect_exec(bContext *C, wmOperator *op)
extend = RNA_boolean_get(op->ptr, "extend");
if (!extend)
- ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_CLEAR);
+ ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, true, ACHANNEL_SETFLAG_CLEAR);
if (gesture_mode == GESTURE_MODAL_SELECT)
selectmode = ACHANNEL_SETFLAG_ADD;
@@ -2544,7 +2638,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
}
else {
/* select AnimData block by itself */
- ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+ ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
ale->adt->flag |= ADT_UI_SELECTED;
}
@@ -2599,7 +2693,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
FCurve *fcu;
/* deselect all other channels */
- ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+ ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
if (pchan) ED_pose_deselectall(ob, 0);
/* only select channels in group and group itself */
@@ -2609,7 +2703,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
}
else {
/* select group by itself */
- ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+ ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
if (pchan) ED_pose_deselectall(ob, 0);
agrp->flag |= AGRP_SELECTED;
@@ -2639,7 +2733,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
}
else {
/* select F-Curve by itself */
- ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+ ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
fcu->flag |= FCURVE_SELECTED;
}
@@ -2661,7 +2755,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
}
else {
/* select ShapeKey by itself */
- ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+ ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
kb->flag |= KEYBLOCK_SEL;
}
@@ -2691,7 +2785,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
}
else {
/* select layer by itself */
- ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+ ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
gpl->flag |= GP_LAYER_SELECT;
}
@@ -2721,7 +2815,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
}
else {
/* select layer by itself */
- ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+ ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
masklay->flag |= MASK_LAYERFLAG_SELECT;
}
@@ -2806,10 +2900,10 @@ static void ANIM_OT_channels_click(wmOperatorType *ot)
/* properties */
/* NOTE: don't save settings, otherwise, can end up with some weird behaviour (sticky extend) */
- prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
+ prop = RNA_def_boolean(ot->srna, "extend", false, "Extend Select", ""); // SHIFTKEY
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(ot->srna, "children_only", 0, "Select Children Only", ""); // CTRLKEY|SHIFTKEY
+ prop = RNA_def_boolean(ot->srna, "children_only", false, "Select Children Only", ""); // CTRLKEY|SHIFTKEY
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
@@ -2824,6 +2918,8 @@ void ED_operatortypes_animchannels(void)
WM_operatortype_append(ANIM_OT_channels_click);
WM_operatortype_append(ANIM_OT_channels_rename);
+ WM_operatortype_append(ANIM_OT_channels_find);
+
WM_operatortype_append(ANIM_OT_channels_setting_enable);
WM_operatortype_append(ANIM_OT_channels_setting_disable);
WM_operatortype_append(ANIM_OT_channels_setting_toggle);
@@ -2852,8 +2948,7 @@ void ED_keymap_animchannels(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_find(keyconf, "Animation Channels", 0, 0);
wmKeyMapItem *kmi;
-
- /* selection */
+
/* click-select */
/* XXX for now, only leftmouse.... */
WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, 0, 0);
@@ -2862,6 +2957,10 @@ void ED_keymap_animchannels(wmKeyConfig *keyconf)
/* rename */
WM_keymap_add_item(keymap, "ANIM_OT_channels_rename", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
+ WM_keymap_add_item(keymap, "ANIM_OT_channels_rename", LEFTMOUSE, KM_DBL_CLICK, 0, 0);
+
+ /* find (i.e. a shortcut for setting the name filter) */
+ WM_keymap_add_item(keymap, "ANIM_OT_channels_find", FKEY, KM_PRESS, KM_CTRL, 0);
/* deselect all */
WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", AKEY, KM_PRESS, 0, 0);
diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c
index 5bffdfbcc4b..1ca2c247ff0 100644
--- a/source/blender/editors/animation/anim_deps.c
+++ b/source/blender/editors/animation/anim_deps.c
@@ -349,8 +349,15 @@ void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data)
{
bAnimListElem *ale;
- if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
+ if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
+#ifdef DEBUG
+ /* quiet assert */
+ for (ale = anim_data->first; ale; ale = ale->next) {
+ ale->update = 0;
+ }
+#endif
return;
+ }
for (ale = anim_data->first; ale; ale = ale->next) {
FCurve *fcu = ale->key_data;
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index 9fec1d28a29..dad25eb04af 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -2595,7 +2595,7 @@ static size_t animdata_filter_remove_duplis(ListBase *anim_data)
* - just use ale->data for now, though it would be nicer to involve
* ale->type in combination too to capture corner cases (where same data performs differently)
*/
- if (BLI_gset_reinsert(gs, ale->data, NULL)) {
+ if (BLI_gset_add(gs, ale->data)) {
/* this entry is 'unique' and can be kept */
items++;
}
diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c
index c8db4ae5892..481912f4d1f 100644
--- a/source/blender/editors/animation/drivers.c
+++ b/source/blender/editors/animation/drivers.c
@@ -109,7 +109,7 @@ FCurve *verify_driver_fcurve(ID *id, const char rna_path[], const int array_inde
fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED);
/* store path - make copy, and store that */
- fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path));
+ fcu->rna_path = BLI_strdup(rna_path);
fcu->array_index = array_index;
/* if add is negative, don't init this data yet, since it will be filled in by the pasted driver */
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index 78c87d58766..878c9193b23 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -186,7 +186,7 @@ FCurve *verify_fcurve(bAction *act, const char group[], PointerRNA *ptr,
fcu->flag |= FCURVE_ACTIVE; /* first one added active */
/* store path - make copy, and store that */
- fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path));
+ fcu->rna_path = BLI_strdup(rna_path);
fcu->array_index = array_index;
/* if a group name has been provided, try to add or find a group, then add F-Curve to it */
@@ -394,23 +394,42 @@ int insert_vert_fcurve(FCurve *fcu, float x, float y, short flag)
beztr.vec[2][0] = x + 1.0f;
beztr.vec[2][1] = y;
beztr.f1 = beztr.f2 = beztr.f3 = SELECT;
-
+
+ /* set default handle types and interpolation mode */
if (flag & INSERTKEY_NO_USERPREF) {
+ /* for Py-API, we want scripts to have predictable behaviour,
+ * hence the option to not depend on the userpref defaults
+ */
beztr.h1 = beztr.h2 = HD_AUTO_ANIM;
beztr.ipo = BEZT_IPO_BEZ;
}
else {
+ /* for UI usage - defaults should come from the */
beztr.h1 = beztr.h2 = U.keyhandles_new; /* use default handle type here */
//BEZKEYTYPE(&beztr)= scene->keytype; /* default keyframe type */
-
+
/* use default interpolation mode, with exceptions for int/discrete values */
beztr.ipo = U.ipo_new;
}
-
- if (fcu->flag & FCURVE_DISCRETE_VALUES)
+
+ /* interpolation type used is constrained by the type of values the curve can take */
+ if (fcu->flag & FCURVE_DISCRETE_VALUES) {
beztr.ipo = BEZT_IPO_CONST;
- else if (beztr.ipo == BEZT_IPO_BEZ && (fcu->flag & FCURVE_INT_VALUES))
+ }
+ else if ((beztr.ipo == BEZT_IPO_BEZ) && (fcu->flag & FCURVE_INT_VALUES)) {
beztr.ipo = BEZT_IPO_LIN;
+ }
+
+ /* set default values for "easing" interpolation mode settings
+ * NOTE: Even if these modes aren't currently used, if users switch
+ * to these later, we want these to work in a sane way out of
+ * the box.
+ */
+ beztr.back = 1.70158f; /* "back" easing - this value used to be used when overshoot=0, but that */
+ /* introduced discontinuities in how the param worked */
+
+ beztr.amplitude = 0.8f; /* "elastic" easing - values here were hand-optimised for a default duration of */
+ beztr.period = 4.1f; /* ~10 frames (typical mograph motion length) */
/* add temp beztriple to keyframes */
a = insert_bezt_fcurve(fcu, &beztr, flag);
diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c
index 3f4f720ccdb..0b115da5ff0 100644
--- a/source/blender/editors/armature/pose_lib.c
+++ b/source/blender/editors/armature/pose_lib.c
@@ -189,7 +189,9 @@ static bAction *poselib_init_new(Object *ob)
/* init object's poselib action (unlink old one if there) */
if (ob->poselib)
id_us_min(&ob->poselib->id);
+
ob->poselib = add_empty_action(G.main, "PoseLib");
+ ob->poselib->idroot = ID_OB;
return ob->poselib;
}
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 61fe30e4ec5..7b78b255a55 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -1042,7 +1042,7 @@ static void curve_rename_fcurves(Curve *cu, ListBase *orig_curves)
{
int nu_index = 0, a, pt_index;
EditNurb *editnurb = cu->editnurb;
- Nurb *nu = editnurb->nurbs.first;
+ Nurb *nu;
CVKeyIndex *keyIndex;
char rna_path[64], orig_rna_path[64];
AnimData *adt = BKE_animdata_from_id(&cu->id);
@@ -5717,7 +5717,7 @@ static int select_more_exec(bContext *C, wmOperator *UNUSED(op))
bp = nu->bp;
selbpoints = BLI_BITMAP_NEW(a, "selectlist");
while (a > 0) {
- if ((!BLI_BITMAP_GET(selbpoints, a)) && (bp->hide == 0) && (bp->f1 & SELECT)) {
+ if ((!BLI_BITMAP_TEST(selbpoints, a)) && (bp->hide == 0) && (bp->f1 & SELECT)) {
/* upper control point */
if (a % nu->pntsu != 0) {
tempbp = bp - 1;
@@ -5730,7 +5730,7 @@ static int select_more_exec(bContext *C, wmOperator *UNUSED(op))
tempbp = bp + nu->pntsu;
if (!(tempbp->f1 & SELECT)) sel = select_bpoint(tempbp, SELECT, SELECT, VISIBLE);
/* make sure selected bpoint is discarded */
- if (sel == 1) BLI_BITMAP_SET(selbpoints, a - nu->pntsu);
+ if (sel == 1) BLI_BITMAP_ENABLE(selbpoints, a - nu->pntsu);
}
/* right control point */
@@ -5814,7 +5814,7 @@ static int select_less_exec(bContext *C, wmOperator *UNUSED(op))
}
else {
bp--;
- if (BLI_BITMAP_GET(selbpoints, a + 1) || ((bp->hide == 0) && (bp->f1 & SELECT))) sel++;
+ if (BLI_BITMAP_TEST(selbpoints, a + 1) || ((bp->hide == 0) && (bp->f1 & SELECT))) sel++;
bp++;
}
@@ -5832,7 +5832,7 @@ static int select_less_exec(bContext *C, wmOperator *UNUSED(op))
}
else {
bp -= nu->pntsu;
- if (BLI_BITMAP_GET(selbpoints, a + nu->pntsu) || ((bp->hide == 0) && (bp->f1 & SELECT))) sel++;
+ if (BLI_BITMAP_TEST(selbpoints, a + nu->pntsu) || ((bp->hide == 0) && (bp->f1 & SELECT))) sel++;
bp += nu->pntsu;
}
@@ -5847,7 +5847,7 @@ static int select_less_exec(bContext *C, wmOperator *UNUSED(op))
if (sel != 4) {
select_bpoint(bp, DESELECT, SELECT, VISIBLE);
- BLI_BITMAP_SET(selbpoints, a);
+ BLI_BITMAP_ENABLE(selbpoints, a);
}
}
else {
diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c
index 1cd690c29d6..8a3d4f3f4f5 100644
--- a/source/blender/editors/curve/editcurve_add.c
+++ b/source/blender/editors/curve/editcurve_add.c
@@ -115,7 +115,7 @@ Nurb *add_nurbs_primitive(bContext *C, Object *obedit, float mat[4][4], int type
BPoint *bp;
Curve *cu = (Curve *)obedit->data;
float vec[3], zvec[3] = {0.0f, 0.0f, 1.0f};
- float umat[4][4] = MAT4_UNITY, viewmat[4][4] = MAT4_UNITY;
+ float umat[4][4], viewmat[4][4];
float fac;
int a, b;
const float grid = 1.0f;
@@ -123,6 +123,9 @@ Nurb *add_nurbs_primitive(bContext *C, Object *obedit, float mat[4][4], int type
const int stype = (type & CU_PRIMITIVE);
const int force_3d = ((Curve *)obedit->data)->flag & CU_3D; /* could be adding to an existing 3D curve */
+ unit_m4(umat);
+ unit_m4(viewmat);
+
if (rv3d) {
copy_m4_m4(viewmat, rv3d->viewmat);
copy_v3_v3(zvec, rv3d->viewinv[2]);
diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c
index 38dd9048790..a69c020b076 100644
--- a/source/blender/editors/gpencil/drawgpencil.c
+++ b/source/blender/editors/gpencil/drawgpencil.c
@@ -676,7 +676,7 @@ static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy,
* ............................ */
/* draw grease-pencil sketches to specified 2d-view that uses ibuf corrections */
-void draw_gpencil_2dimage(const bContext *C)
+void ED_gpencil_draw_2dimage(const bContext *C)
{
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
@@ -685,7 +685,7 @@ void draw_gpencil_2dimage(const bContext *C)
int offsx, offsy, sizex, sizey;
int dflag = GP_DRAWDATA_NOSTATUS;
- gpd = gpencil_data_get_active(C); // XXX
+ gpd = ED_gpencil_data_get_active(C); // XXX
if (gpd == NULL) return;
/* calculate rect */
@@ -738,7 +738,7 @@ void draw_gpencil_2dimage(const bContext *C)
/* draw grease-pencil sketches to specified 2d-view assuming that matrices are already set correctly
* Note: this gets called twice - first time with onlyv2d=1 to draw 'canvas' strokes,
* second time with onlyv2d=0 for screen-aligned strokes */
-void draw_gpencil_view2d(const bContext *C, bool onlyv2d)
+void ED_gpencil_draw_view2d(const bContext *C, bool onlyv2d)
{
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
@@ -748,7 +748,7 @@ void draw_gpencil_view2d(const bContext *C, bool onlyv2d)
/* check that we have grease-pencil stuff to draw */
if (sa == NULL) return;
- gpd = gpencil_data_get_active(C); // XXX
+ gpd = ED_gpencil_data_get_active(C); // XXX
if (gpd == NULL) return;
/* special hack for Image Editor */
@@ -764,7 +764,7 @@ void draw_gpencil_view2d(const bContext *C, bool onlyv2d)
/* draw grease-pencil sketches to specified 3d-view assuming that matrices are already set correctly
* Note: this gets called twice - first time with only3d=1 to draw 3d-strokes,
* second time with only3d=0 for screen-aligned strokes */
-void draw_gpencil_view3d(Scene *scene, View3D *v3d, ARegion *ar, bool only3d)
+void ED_gpencil_draw_view3d(Scene *scene, View3D *v3d, ARegion *ar, bool only3d)
{
bGPdata *gpd;
int dflag = 0;
@@ -772,7 +772,7 @@ void draw_gpencil_view3d(Scene *scene, View3D *v3d, ARegion *ar, bool only3d)
int offsx, offsy, winx, winy;
/* check that we have grease-pencil stuff to draw */
- gpd = gpencil_data_get_active_v3d(scene); // XXX
+ gpd = ED_gpencil_data_get_active_v3d(scene); // XXX
if (gpd == NULL) return;
/* when rendering to the offscreen buffer we don't want to
@@ -799,4 +799,11 @@ void draw_gpencil_view3d(Scene *scene, View3D *v3d, ARegion *ar, bool only3d)
gp_draw_data(gpd, offsx, offsy, winx, winy, CFRA, dflag);
}
+void ED_gpencil_draw_ex(bGPdata *gpd, int winx, int winy, const int cfra)
+{
+ int dflag = GP_DRAWDATA_NOSTATUS | GP_DRAWDATA_ONLYV2D;
+
+ gp_draw_data(gpd, 0, 0, winx, winy, cfra, dflag);
+}
+
/* ************************************************** */
diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c
index f5bf1422488..dba80164e93 100644
--- a/source/blender/editors/gpencil/editaction_gpencil.c
+++ b/source/blender/editors/gpencil/editaction_gpencil.c
@@ -61,30 +61,30 @@
/* Generics - Loopers */
/* Loops over the gp-frames for a gp-layer, and applies the given callback */
-short ED_gplayer_frames_looper(bGPDlayer *gpl, Scene *scene, short (*gpf_cb)(bGPDframe *, Scene *))
+bool ED_gplayer_frames_looper(bGPDlayer *gpl, Scene *scene, short (*gpf_cb)(bGPDframe *, Scene *))
{
bGPDframe *gpf;
/* error checker */
if (gpl == NULL)
- return 0;
+ return false;
/* do loop */
for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
/* execute callback */
if (gpf_cb(gpf, scene))
- return 1;
+ return true;
}
/* nothing to return */
- return 0;
+ return false;
}
/* ****************************************** */
/* Data Conversion Tools */
/* make a listing all the gp-frames in a layer as cfraelems */
-void ED_gplayer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, short onlysel)
+void ED_gplayer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel)
{
bGPDframe *gpf;
CfraElem *ce;
@@ -110,22 +110,22 @@ void ED_gplayer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, short onlysel)
/* Selection Tools */
/* check if one of the frames in this layer is selected */
-short ED_gplayer_frame_select_check(bGPDlayer *gpl)
+bool ED_gplayer_frame_select_check(bGPDlayer *gpl)
{
bGPDframe *gpf;
/* error checking */
if (gpl == NULL)
- return 0;
+ return false;
/* stop at the first one found */
for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
if (gpf->flag & GP_FRAME_SELECT)
- return 1;
+ return true;
}
/* not found */
- return 0;
+ return false;
}
/* helper function - select gp-frame based on SELECT_* mode */
diff --git a/source/blender/editors/gpencil/gpencil_buttons.c b/source/blender/editors/gpencil/gpencil_buttons.c
index 0fe4d7e7157..0acff8fc0a5 100644
--- a/source/blender/editors/gpencil/gpencil_buttons.c
+++ b/source/blender/editors/gpencil/gpencil_buttons.c
@@ -350,7 +350,7 @@ static void draw_gpencil_panel(bContext *C, uiLayout *layout, bGPdata *gpd, Poin
}
}
-void gpencil_panel_standard_header(const bContext *C, Panel *pa)
+void ED_gpencil_panel_standard_header(const bContext *C, Panel *pa)
{
PointerRNA ptr;
RNA_pointer_create((ID *)CTX_wm_screen(C), &RNA_Space, CTX_wm_space_data(C), &ptr);
@@ -359,7 +359,7 @@ void gpencil_panel_standard_header(const bContext *C, Panel *pa)
}
/* Standard panel to be included wherever Grease Pencil is used... */
-void gpencil_panel_standard(const bContext *C, Panel *pa)
+void ED_gpencil_panel_standard(const bContext *C, Panel *pa)
{
bGPdata **gpd_ptr = NULL;
PointerRNA ptr;
@@ -369,7 +369,7 @@ void gpencil_panel_standard(const bContext *C, Panel *pa)
draw_gpencil_space_specials(C, pa->layout);
/* get pointer to Grease Pencil Data */
- gpd_ptr = gpencil_data_get_pointers((bContext *)C, &ptr);
+ gpd_ptr = ED_gpencil_data_get_pointers((bContext *)C, &ptr);
if (gpd_ptr)
draw_gpencil_panel((bContext *)C, pa->layout, *gpd_ptr, &ptr);
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index d25de906e31..b725f5c773a 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -86,7 +86,7 @@
/* Context Wrangling... */
/* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */
-bGPdata **gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr)
+bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr)
{
ID *screen_id = (ID *)CTX_wm_screen(C);
Scene *scene = CTX_data_scene(C);
@@ -180,19 +180,19 @@ bGPdata **gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr)
}
/* Get the active Grease Pencil datablock */
-bGPdata *gpencil_data_get_active(const bContext *C)
+bGPdata *ED_gpencil_data_get_active(const bContext *C)
{
- bGPdata **gpd_ptr = gpencil_data_get_pointers(C, NULL);
+ bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
return (gpd_ptr) ? *(gpd_ptr) : NULL;
}
/* needed for offscreen rendering */
-bGPdata *gpencil_data_get_active_v3d(Scene *scene)
+bGPdata *ED_gpencil_data_get_active_v3d(Scene *scene)
{
Base *base = scene->basact;
bGPdata *gpd = NULL;
/* We have to make sure active object is actually visible and selected, else we must use default scene gpd,
- * to be consistent with gpencil_data_get_active's behavior.
+ * to be consistent with ED_gpencil_data_get_active's behavior.
*/
if (base && (scene->lay & base->lay) && (base->object->flag & SELECT)) {
gpd = base->object->gpd;
@@ -207,7 +207,7 @@ bGPdata *gpencil_data_get_active_v3d(Scene *scene)
static int gp_add_poll(bContext *C)
{
/* the base line we have is that we have somewhere to add Grease Pencil data */
- return gpencil_data_get_pointers(C, NULL) != NULL;
+ return ED_gpencil_data_get_pointers(C, NULL) != NULL;
}
/* ******************* Add New Data ************************ */
@@ -215,7 +215,7 @@ static int gp_add_poll(bContext *C)
/* add new datablock - wrapper around API */
static int gp_data_add_exec(bContext *C, wmOperator *op)
{
- bGPdata **gpd_ptr = gpencil_data_get_pointers(C, NULL);
+ bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
if (gpd_ptr == NULL) {
BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
@@ -253,7 +253,7 @@ void GPENCIL_OT_data_add(wmOperatorType *ot)
/* poll callback for adding data/layers - special */
static int gp_data_unlink_poll(bContext *C)
{
- bGPdata **gpd_ptr = gpencil_data_get_pointers(C, NULL);
+ bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
/* if we have access to some active data, make sure there's a datablock before enabling this */
return (gpd_ptr && *gpd_ptr);
@@ -263,7 +263,7 @@ static int gp_data_unlink_poll(bContext *C)
/* unlink datablock - wrapper around API */
static int gp_data_unlink_exec(bContext *C, wmOperator *op)
{
- bGPdata **gpd_ptr = gpencil_data_get_pointers(C, NULL);
+ bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
if (gpd_ptr == NULL) {
BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
@@ -301,7 +301,7 @@ void GPENCIL_OT_data_unlink(wmOperatorType *ot)
/* add new layer - wrapper around API */
static int gp_layer_add_exec(bContext *C, wmOperator *op)
{
- bGPdata **gpd_ptr = gpencil_data_get_pointers(C, NULL);
+ bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
/* if there's no existing Grease-Pencil data there, add some */
if (gpd_ptr == NULL) {
@@ -337,7 +337,7 @@ void GPENCIL_OT_layer_add(wmOperatorType *ot)
static int gp_actframe_delete_poll(bContext *C)
{
- bGPdata *gpd = gpencil_data_get_active(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDlayer *gpl = gpencil_layer_getactive(gpd);
/* only if there's an active layer with an active frame */
@@ -348,7 +348,7 @@ static int gp_actframe_delete_poll(bContext *C)
static int gp_actframe_delete_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- bGPdata *gpd = gpencil_data_get_active(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDlayer *gpl = gpencil_layer_getactive(gpd);
bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0);
@@ -1559,7 +1559,7 @@ static void gp_convert_set_end_frame(struct Main *UNUSED(main), struct Scene *UN
static int gp_convert_poll(bContext *C)
{
- bGPdata *gpd = gpencil_data_get_active(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDlayer *gpl = NULL;
bGPDframe *gpf = NULL;
ScrArea *sa = CTX_wm_area(C);
@@ -1578,7 +1578,7 @@ static int gp_convert_poll(bContext *C)
static int gp_convert_layer_exec(bContext *C, wmOperator *op)
{
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_timing_data");
- bGPdata *gpd = gpencil_data_get_active(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDlayer *gpl = gpencil_layer_getactive(gpd);
Scene *scene = CTX_data_scene(C);
const int mode = RNA_enum_get(op->ptr, "type");
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 2f941142d9e..4910396ac6f 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -177,7 +177,7 @@ static int gpencil_draw_poll(bContext *C)
{
if (ED_operator_regionactive(C)) {
/* check if current context can support GPencil data */
- if (gpencil_data_get_pointers(C, NULL) != NULL) {
+ if (ED_gpencil_data_get_pointers(C, NULL) != NULL) {
/* check if Grease Pencil isn't already running */
if (ED_gpencil_session_active() == 0)
return 1;
@@ -1161,7 +1161,7 @@ static int gp_session_initdata(bContext *C, tGPsdata *p)
}
/* get gp-data */
- gpd_ptr = gpencil_data_get_pointers(C, &p->ownerPtr);
+ gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr);
if (gpd_ptr == NULL) {
p->status = GP_STATUS_ERROR;
if (G.debug & G_DEBUG)
diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c
index 136e9da389d..ff92d69f39d 100644
--- a/source/blender/editors/gpencil/gpencil_undo.c
+++ b/source/blender/editors/gpencil/gpencil_undo.c
@@ -70,7 +70,7 @@ int ED_undo_gpencil_step(bContext *C, int step, const char *name)
{
bGPdata **gpd_ptr = NULL, *new_gpd = NULL;
- gpd_ptr = gpencil_data_get_pointers(C, NULL);
+ gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
if (step == 1) { /* undo */
//printf("\t\tGP - undo step\n");
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index 02cc86eb55c..956ec308daa 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -467,13 +467,13 @@ void ANIM_channel_draw_widgets(struct bContext *C, bAnimContext *ac, bAnimListEl
*
* - setting: eAnimChannel_Settings
*/
-short ANIM_channel_setting_get(bAnimContext *ac, bAnimListElem *ale, int setting);
+short ANIM_channel_setting_get(bAnimContext *ac, bAnimListElem *ale, eAnimChannel_Settings setting);
/* Change value of some setting for a channel
* - setting: eAnimChannel_Settings
* - mode: eAnimChannels_SetFlag
*/
-void ANIM_channel_setting_set(bAnimContext *ac, bAnimListElem *ale, int setting, short mode);
+void ANIM_channel_setting_set(bAnimContext *ac, bAnimListElem *ale, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode);
/* Flush visibility (for Graph Editor) changes up/down hierarchy for changes in the given setting
@@ -485,14 +485,14 @@ void ANIM_channel_setting_set(bAnimContext *ac, bAnimListElem *ale, int setting,
* - setting: type of setting to set
* - on: whether the visibility setting has been enabled or disabled
*/
-void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, int setting, short mode);
+void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode);
/* Deselect all animation channels */
-void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, short test, short sel);
+void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types datatype, bool test, eAnimChannels_SetFlag sel);
/* Set the 'active' channel of type channel_type, in the given action */
-void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int filter, void *channel_data, short channel_type);
+void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datatype, eAnimFilter_Flags filter, void *channel_data, eAnim_ChannelType channel_type);
/* Delete the F-Curve from the given AnimData block (if possible), as appropriate according to animation context */
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index e31221a42df..63ffa1bef0c 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -65,9 +65,9 @@ typedef struct tGPspoint {
/* ----------- Grease Pencil Tools/Context ------------- */
-struct bGPdata **gpencil_data_get_pointers(const struct bContext *C, struct PointerRNA *ptr);
-struct bGPdata *gpencil_data_get_active(const struct bContext *C);
-struct bGPdata *gpencil_data_get_active_v3d(struct Scene *scene); /* for offscreen rendering */
+struct bGPdata **ED_gpencil_data_get_pointers(const struct bContext *C, struct PointerRNA *ptr);
+struct bGPdata *ED_gpencil_data_get_active(const struct bContext *C);
+struct bGPdata *ED_gpencil_data_get_active_v3d(struct Scene *scene); /* for offscreen rendering */
/* ----------- Grease Pencil Operators ----------------- */
@@ -77,19 +77,20 @@ void ED_operatortypes_gpencil(void);
/* ------------ Grease-Pencil Drawing API ------------------ */
/* drawgpencil.c */
-void draw_gpencil_2dimage(const struct bContext *C);
-void draw_gpencil_view2d(const struct bContext *C, bool onlyv2d);
-void draw_gpencil_view3d(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, bool only3d);
+void ED_gpencil_draw_2dimage(const struct bContext *C);
+void ED_gpencil_draw_view2d(const struct bContext *C, bool onlyv2d);
+void ED_gpencil_draw_view3d(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, bool only3d);
+void ED_gpencil_draw_ex(struct bGPdata *gpd, int winx, int winy, const int cfra);
-void gpencil_panel_standard_header(const struct bContext *C, struct Panel *pa);
-void gpencil_panel_standard(const struct bContext *C, struct Panel *pa);
+void ED_gpencil_panel_standard_header(const struct bContext *C, struct Panel *pa);
+void ED_gpencil_panel_standard(const struct bContext *C, struct Panel *pa);
/* ----------- Grease-Pencil AnimEdit API ------------------ */
-short ED_gplayer_frames_looper(struct bGPDlayer *gpl, struct Scene *scene,
+bool ED_gplayer_frames_looper(struct bGPDlayer *gpl, struct Scene *scene,
short (*gpf_cb)(struct bGPDframe *, struct Scene *));
-void ED_gplayer_make_cfra_list(struct bGPDlayer *gpl, ListBase *elems, short onlysel);
+void ED_gplayer_make_cfra_list(struct bGPDlayer *gpl, ListBase *elems, bool onlysel);
-short ED_gplayer_frame_select_check(struct bGPDlayer *gpl);
+bool ED_gplayer_frame_select_check(struct bGPDlayer *gpl);
void ED_gplayer_frame_select_set(struct bGPDlayer *gpl, short mode);
void ED_gplayer_frames_select_border(struct bGPDlayer *gpl, float min, float max, short select_mode);
void ED_gpencil_select_frames(struct bGPDlayer *gpl, short select_mode);
diff --git a/source/blender/editors/include/ED_paint.h b/source/blender/editors/include/ED_paint.h
new file mode 100644
index 00000000000..d7e84d8f50d
--- /dev/null
+++ b/source/blender/editors/include/ED_paint.h
@@ -0,0 +1,61 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file ED_paint.h
+ * \ingroup editors
+ */
+
+#ifndef __ED_PAINT_H__
+#define __ED_PAINT_H__
+
+struct bContext;
+struct RegionView3D;
+struct wmKeyConfig;
+
+/* paint_ops.c */
+void ED_operatortypes_paint(void);
+void ED_keymap_paint(struct wmKeyConfig *keyconf);
+
+/* paint_undo.c */
+enum {
+ UNDO_PAINT_IMAGE = 0,
+ UNDO_PAINT_MESH = 1,
+};
+
+typedef void (*UndoRestoreCb)(struct bContext *C, struct ListBase *lb);
+typedef void (*UndoFreeCb)(struct ListBase *lb);
+
+int ED_undo_paint_step(struct bContext *C, int type, int step, const char *name);
+void ED_undo_paint_step_num(struct bContext *C, int type, int num);
+const char *ED_undo_paint_get_name(struct bContext *C, int type, int nr, int *active);
+void ED_undo_paint_free(void);
+int ED_undo_paint_valid(int type, const char *name);
+bool ED_undo_paint_empty(int type);
+void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free);
+void ED_undo_paint_push_end(int type);
+
+/* paint_image.c */
+/* image painting specific undo */
+void ED_image_undo_restore(struct bContext *C, struct ListBase *lb);
+void ED_image_undo_free(struct ListBase *lb);
+void ED_imapaint_clear_partial_redraw(void);
+void ED_imapaint_dirty_region(struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h);
+
+#endif /* __ED_PAINT_H__ */
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index 060702f6d71..d31be3e961f 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -60,6 +60,7 @@ void ED_region_init(struct bContext *C, struct ARegion *ar);
void ED_region_tag_redraw(struct ARegion *ar);
void ED_region_tag_redraw_partial(struct ARegion *ar, struct rcti *rct);
void ED_region_tag_redraw_overlay(struct ARegion *ar);
+void ED_region_tag_refresh_ui(struct ARegion *ar);
void ED_region_panels_init(struct wmWindowManager *wm, struct ARegion *ar);
void ED_region_panels(const struct bContext *C, struct ARegion *ar, int vertical, const char *context, int contextnr);
void ED_region_header_init(struct ARegion *ar);
diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h
index 2e092f7e7fe..85ff9b5d246 100644
--- a/source/blender/editors/include/ED_sculpt.h
+++ b/source/blender/editors/include/ED_sculpt.h
@@ -32,47 +32,17 @@
struct ARegion;
struct bContext;
-struct MultiresModifierData;
struct Object;
struct RegionView3D;
-struct wmKeyConfig;
-struct wmWindowManager;
struct ViewContext;
struct rcti;
/* sculpt.c */
void ED_operatortypes_sculpt(void);
-void sculpt_get_redraw_planes(float planes[4][4], struct ARegion *ar,
- struct RegionView3D *rv3d, struct Object *ob);
-void ED_sculpt_get_average_stroke(struct Object *ob, float stroke[3]);
+void ED_sculpt_redraw_planes_get(float planes[4][4], struct ARegion *ar,
+ struct RegionView3D *rv3d, struct Object *ob);
+void ED_sculpt_stroke_get_average(struct Object *ob, float stroke[3]);
bool ED_sculpt_minmax(struct bContext *C, float min[3], float max[3]);
-int do_sculpt_mask_box_select(struct bContext *C, struct ViewContext *vc, struct rcti *rect, bool select, bool extend);
+int ED_sculpt_mask_box_select(struct bContext *C, struct ViewContext *vc, const struct rcti *rect, bool select, bool extend);
-/* paint_ops.c */
-void ED_operatortypes_paint(void);
-void ED_keymap_paint(struct wmKeyConfig *keyconf);
-
-/* paint_undo.c */
-#define UNDO_PAINT_IMAGE 0
-#define UNDO_PAINT_MESH 1
-
-typedef void (*UndoRestoreCb)(struct bContext *C, struct ListBase *lb);
-typedef void (*UndoFreeCb)(struct ListBase *lb);
-
-int ED_undo_paint_step(struct bContext *C, int type, int step, const char *name);
-void ED_undo_paint_step_num(struct bContext *C, int type, int num);
-const char *ED_undo_paint_get_name(struct bContext *C, int type, int nr, int *active);
-void ED_undo_paint_free(void);
-int ED_undo_paint_valid(int type, const char *name);
-bool ED_undo_paint_empty(int type);
-void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free);
-void ED_undo_paint_push_end(int type);
-
-/* image painting specific undo */
-void ED_image_undo_restore(struct bContext *C, struct ListBase *lb);
-void ED_image_undo_free(struct ListBase *lb);
-void ED_imapaint_clear_partial_redraw(void);
-void ED_imapaint_dirty_region(struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h);
-
-
-#endif
+#endif /* __ED_SCULPT_H__ */
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 377bb7cebb1..43059d9af91 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -269,7 +269,7 @@ bool ED_view3d_autodist_depth_seg(struct ARegion *ar, const int mval_sta[2], con
/* select */
#define MAXPICKBUF 10000
-short view3d_opengl_select(struct ViewContext *vc, unsigned int *buffer, unsigned int bufsize, rcti *input);
+short view3d_opengl_select(struct ViewContext *vc, unsigned int *buffer, unsigned int bufsize, const rcti *input);
/* view3d_select.c */
float ED_view3d_select_dist_px(void);
@@ -346,7 +346,7 @@ void ED_view3d_distance_set(struct RegionView3D *rv3d, const float dist);
float ED_scene_grid_scale(struct Scene *scene, const char **grid_unit);
float ED_view3d_grid_scale(struct Scene *scene, struct View3D *v3d, const char **grid_unit);
-void ED_scene_draw_fps(struct Scene *scene, struct rcti *rect);
+void ED_scene_draw_fps(struct Scene *scene, const struct rcti *rect);
/* view matrix properties utilities */
/* unused */
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 8de9650ddef..480e8f1e7b8 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -380,8 +380,10 @@ void uiPupBlockClose(struct bContext *C, uiBlock *block);
* */
uiBlock *uiBeginBlock(const struct bContext *C, struct ARegion *region, const char *name, short dt);
+void uiEndBlock_ex(const struct bContext *C, uiBlock *block, const int xy[2]);
void uiEndBlock(const struct bContext *C, uiBlock *block);
void uiDrawBlock(const struct bContext *C, struct uiBlock *block);
+void uiBlockUpdateFromOld(const struct bContext *C, struct uiBlock *block);
uiBlock *uiGetBlock(const char *name, struct ARegion *ar);
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 19d4e32152f..c857150782e 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -295,9 +295,8 @@ void ui_bounds_block(uiBlock *block)
block->safety.ymax = block->rect.ymax + xof;
}
-static void ui_centered_bounds_block(const bContext *C, uiBlock *block)
+static void ui_centered_bounds_block(wmWindow *window, uiBlock *block)
{
- wmWindow *window = CTX_wm_window(C);
int xmax, ymax;
int startx, starty;
int width, height;
@@ -322,9 +321,9 @@ static void ui_centered_bounds_block(const bContext *C, uiBlock *block)
ui_bounds_block(block);
}
-static void ui_popup_bounds_block(const bContext *C, uiBlock *block, eBlockBoundsCalc bounds_calc)
+static void ui_popup_bounds_block(wmWindow *window, uiBlock *block,
+ eBlockBoundsCalc bounds_calc, const int xy[2])
{
- wmWindow *window = CTX_wm_window(C);
int startx, starty, endx, endy, width, height, oldwidth, oldheight;
int oldbounds, xmax, ymax;
const int margin = UI_SCREEN_MARGIN;
@@ -362,8 +361,8 @@ static void ui_popup_bounds_block(const bContext *C, uiBlock *block, eBlockBound
/* offset block based on mouse position, user offset is scaled
* along in case we resized the block in ui_text_bounds_block */
- startx = window->eventstate->x + block->rect.xmin + (block->mx * width) / oldwidth;
- starty = window->eventstate->y + block->rect.ymin + (block->my * height) / oldheight;
+ startx = xy[0] + block->rect.xmin + (block->mx * width) / oldwidth;
+ starty = xy[1] + block->rect.ymin + (block->my * height) / oldheight;
if (startx < margin)
startx = margin;
@@ -1083,29 +1082,50 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block)
}
}
-void uiEndBlock(const bContext *C, uiBlock *block)
+void uiBlockUpdateFromOld(const bContext *C, uiBlock *block)
{
- const bool has_old = (block->oldblock != NULL);
- /* avoid searches when old/new lists align */
- uiBut *but_old = has_old ? block->oldblock->buttons.first : NULL;
-
+ uiBut *but_old;
uiBut *but;
- Scene *scene = CTX_data_scene(C);
+ if (!block->oldblock)
+ return;
+
+ but_old = block->oldblock->buttons.first;
- if (has_old && BLI_listbase_is_empty(&block->oldblock->butstore) == false) {
+ if (BLI_listbase_is_empty(&block->oldblock->butstore) == false) {
UI_butstore_update(block);
}
+ for (but = block->buttons.first; but; but = but->next) {
+ if (ui_but_update_from_old_block(C, block, &but, &but_old)) {
+ ui_check_but(but);
+ }
+ }
+
+ block->auto_open = block->oldblock->auto_open;
+ block->auto_open_last = block->oldblock->auto_open_last;
+ block->tooltipdisabled = block->oldblock->tooltipdisabled;
+ copy_v3_v3(ui_block_hsv_get(block),
+ ui_block_hsv_get(block->oldblock));
+
+ block->oldblock = NULL;
+}
+
+void uiEndBlock_ex(const bContext *C, uiBlock *block, const int xy[2])
+{
+ wmWindow *window = CTX_wm_window(C);
+ Scene *scene = CTX_data_scene(C);
+ uiBut *but;
+
+ BLI_assert(block->active);
+
+ uiBlockUpdateFromOld(C, block);
+
/* inherit flags from 'old' buttons that was drawn here previous, based
* on matching buttons, we need this to make button event handling non
* blocking, while still allowing buttons to be remade each redraw as it
* is expected by blender code */
for (but = block->buttons.first; but; but = but->next) {
- if (has_old && ui_but_update_from_old_block(C, block, &but, &but_old)) {
- ui_check_but(but);
- }
-
/* temp? Proper check for graying out */
if (but->optype) {
wmOperatorType *ot = but->optype;
@@ -1125,15 +1145,7 @@ void uiEndBlock(const bContext *C, uiBlock *block)
ui_but_anim_flag(but, (scene) ? scene->r.cfra : 0.0f);
}
- if (block->oldblock) {
- block->auto_open = block->oldblock->auto_open;
- block->auto_open_last = block->oldblock->auto_open_last;
- block->tooltipdisabled = block->oldblock->tooltipdisabled;
- copy_v3_v3(ui_block_hsv_get(block),
- ui_block_hsv_get(block->oldblock));
- block->oldblock = NULL;
- }
/* handle pending stuff */
if (block->layouts.first) {
@@ -1159,13 +1171,13 @@ void uiEndBlock(const bContext *C, uiBlock *block)
ui_text_bounds_block(block, 0.0f);
break;
case UI_BLOCK_BOUNDS_POPUP_CENTER:
- ui_centered_bounds_block(C, block);
+ ui_centered_bounds_block(window, block);
break;
/* fallback */
case UI_BLOCK_BOUNDS_POPUP_MOUSE:
case UI_BLOCK_BOUNDS_POPUP_MENU:
- ui_popup_bounds_block(C, block, block->bounds_type);
+ ui_popup_bounds_block(window, block, block->bounds_type, xy);
break;
}
@@ -1179,6 +1191,13 @@ void uiEndBlock(const bContext *C, uiBlock *block)
block->endblock = 1;
}
+void uiEndBlock(const bContext *C, uiBlock *block)
+{
+ wmWindow *window = CTX_wm_window(C);
+
+ uiEndBlock_ex(C, block, &window->eventstate->x);
+}
+
/* ************** BLOCK DRAWING FUNCTION ************* */
void ui_fontscale(short *points, float aspect)
@@ -1796,18 +1815,7 @@ void ui_set_but_val(uiBut *but, double value)
value = (char)floor(value + 0.5);
}
else if (but->pointype == UI_BUT_POIN_SHORT) {
- /* gcc 3.2.1 seems to have problems
- * casting a double like 32772.0 to
- * a short so we cast to an int, then
- * to a short.
- *
- * Update: even in gcc.4.6 using intermediate int cast gives -32764,
- * where as a direct cast from double to short gives -32768,
- * if this difference isn't important we could remove this hack,
- * since we dont support gcc3 anymore - Campbell */
- int gcckludge;
- gcckludge = (int) floor(value + 0.5);
- value = (short)gcckludge;
+ value = (short)floor(value + 0.5);
}
else if (but->pointype == UI_BUT_POIN_INT)
value = (int)floor(value + 0.5);
@@ -1893,8 +1901,7 @@ void ui_convert_to_unit_alt_name(uiBut *but, char *str, size_t maxlen)
int unit_type = uiButGetUnitType(but);
char *orig_str;
- orig_str = MEM_callocN(sizeof(char) * maxlen + 1, "textedit sub str");
- memcpy(orig_str, str, maxlen);
+ orig_str = BLI_strdup(str);
bUnit_ToUnitAltName(str, maxlen, orig_str, unit->system, RNA_SUBTYPE_UNIT_VALUE(unit_type));
@@ -1984,7 +1991,7 @@ void ui_get_but_string_ex(uiBut *but, char *str, const size_t maxlen, const int
}
else if (buf && buf != str) {
/* string was too long, we have to truncate */
- memcpy(str, buf, MIN2(maxlen, (size_t)buf_len + 1));
+ memcpy(str, buf, MIN2(maxlen, (size_t)(buf_len + 1)));
MEM_freeN((void *)buf);
}
}
@@ -2430,6 +2437,7 @@ void uiBlockSetRegion(uiBlock *block, ARegion *region)
if (oldblock) {
oldblock->active = 0;
oldblock->panel = NULL;
+ oldblock->handle = NULL;
}
/* at the beginning of the list! for dynamical menus/blocks */
@@ -3170,12 +3178,12 @@ static void ui_def_but_rna__menu(bContext *UNUSED(C), uiLayout *layout, void *bu
}
else {
if (item->icon) {
- uiDefIconTextButF(block, BUTM, B_NOP, item->icon, item->name, 0, 0,
- UI_UNIT_X * 5, UI_UNIT_Y, &handle->retvalue, (float) item->value, 0.0, 0, -1, item->description);
+ uiDefIconTextButI(block, BUTM, B_NOP, item->icon, item->name, 0, 0,
+ UI_UNIT_X * 5, UI_UNIT_Y, &handle->retvalue, item->value, 0.0, 0, -1, item->description);
}
else {
- uiDefButF(block, BUTM, B_NOP, item->name, 0, 0,
- UI_UNIT_X * 5, UI_UNIT_X, &handle->retvalue, (float) item->value, 0.0, 0, -1, item->description);
+ uiDefButI(block, BUTM, B_NOP, item->name, 0, 0,
+ UI_UNIT_X * 5, UI_UNIT_X, &handle->retvalue, item->value, 0.0, 0, -1, item->description);
}
}
}
@@ -3783,8 +3791,7 @@ void uiBlockSetDirection(uiBlock *block, char direction)
/* this call escapes if there's alignment flags */
void uiBlockFlipOrder(uiBlock *block)
{
- ListBase lb;
- uiBut *but, *next;
+ uiBut *but;
float centy, miny = 10000, maxy = -10000;
if (U.uiflag & USER_MENUFIXEDORDER)
@@ -3806,15 +3813,7 @@ void uiBlockFlipOrder(uiBlock *block)
}
/* also flip order in block itself, for example for arrowkey */
- BLI_listbase_clear(&lb);
- but = block->buttons.first;
- while (but) {
- next = but->next;
- BLI_remlink(&block->buttons, but);
- BLI_addtail(&lb, but);
- but = next;
- }
- block->buttons = lb;
+ BLI_listbase_reverse(&block->buttons);
}
diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c
index 47334d6f77c..cc9f8ff00a9 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -587,7 +587,7 @@ void ui_draw_but_WAVEFORM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol),
int i, c;
float w, w3, h, alpha, yofs;
GLint scissor[4];
- float colors[3][3] = MAT3_UNITY;
+ float colors[3][3];
float colorsycc[3][3] = {{1, 0, 1}, {1, 1, 0}, {0, 1, 1}};
float colors_alpha[3][3], colorsycc_alpha[3][3]; /* colors pre multiplied by alpha for speed up */
float min, max;
@@ -609,6 +609,8 @@ void ui_draw_but_WAVEFORM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol),
/* log scale for alpha */
alpha = scopes->wavefrm_alpha * scopes->wavefrm_alpha;
+ unit_m3(colors);
+
for (c = 0; c < 3; c++) {
for (i = 0; i < 3; i++) {
colors_alpha[c][i] = colors[c][i] * alpha;
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index f6df5a921d9..e04fef329b1 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -2372,6 +2372,18 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
data->str = NULL;
}
+#ifdef USE_DRAG_MULTINUM
+ /* this can happen from multi-drag */
+ if (data->applied_interactive) {
+ /* remove any small changes so canceling edit doesn't restore invalid value: T40538 */
+ data->cancel = true;
+ ui_apply_button(C, but->block, but, data, true);
+ data->cancel = false;
+
+ data->applied_interactive = false;
+ }
+#endif
+
/* retrieve string */
data->maxlen = ui_get_but_string_max_length(but);
data->str = MEM_callocN(sizeof(char) * data->maxlen + 1, "textedit str");
@@ -2392,11 +2404,6 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
data->selextend = 0;
data->selstartx = 0.0f;
-#ifdef USE_DRAG_MULTINUM
- /* this can happen from multi-drag */
- data->applied_interactive = false;
-#endif
-
/* set cursor pos to the end of the text */
but->editstr = data->str;
but->pos = len;
@@ -3308,6 +3315,11 @@ static bool ui_numedit_but_NUM(uiBut *but, uiHandleButtonData *data,
if (data->draglock) {
if (abs(mx - data->dragstartx) <= 3)
return changed;
+#ifdef USE_DRAG_MULTINUM
+ if (ELEM(data->multi_data.init, BUTTON_MULTI_INIT_UNSET, BUTTON_MULTI_INIT_SETUP)) {
+ return changed;
+ }
+#endif
data->draglock = false;
data->dragstartx = mx; /* ignore mouse movement within drag-lock */
@@ -5504,7 +5516,6 @@ static uiBlock *menu_change_shortcut(bContext *C, ARegion *ar, void *arg)
uiItemR(layout, &ptr, "type", UI_ITEM_R_FULL_EVENT | UI_ITEM_R_IMMEDIATE, "", ICON_NONE);
uiPopupBoundsBlock(block, 6, -50, 26);
- uiEndBlock(C, block);
return block;
}
@@ -5549,11 +5560,22 @@ static uiBlock *menu_add_shortcut(bContext *C, ARegion *ar, void *arg)
uiItemR(layout, &ptr, "type", UI_ITEM_R_FULL_EVENT | UI_ITEM_R_IMMEDIATE, "", ICON_NONE);
uiPopupBoundsBlock(block, 6, -50, 26);
- uiEndBlock(C, block);
return block;
}
+static void menu_add_shortcut_cancel(struct bContext *C, void *arg1)
+{
+ uiBut *but = (uiBut *)arg1;
+ wmKeyMap *km;
+ wmKeyMapItem *kmi;
+ IDProperty *prop = (but->opptr) ? but->opptr->data : NULL;
+ int kmi_id = WM_key_event_operator_id(C, but->optype->idname, but->opcontext, prop, true, &km);
+
+ kmi = WM_keymap_item_find_id(km, kmi_id);
+ WM_keymap_remove_item(km, kmi);
+}
+
static void popup_change_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2))
{
uiBut *but = (uiBut *)arg1;
@@ -5579,7 +5601,7 @@ static void popup_add_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2))
{
uiBut *but = (uiBut *)arg1;
button_timers_tooltip_remove(C, but);
- uiPupBlock(C, menu_add_shortcut, but);
+ uiPupBlockEx(C, menu_add_shortcut, NULL, menu_add_shortcut_cancel, but);
}
/**
@@ -6894,7 +6916,7 @@ void uiContextActivePropertyHandle(bContext *C)
* operator redo panel - campbell */
uiBlock *block = activebut->block;
if (block->handle_func) {
- block->handle_func(C, block->handle_func_arg, 0);
+ block->handle_func(C, block->handle_func_arg, activebut->retval);
}
}
}
@@ -7034,7 +7056,7 @@ void ui_button_activate_do(bContext *C, ARegion *ar, uiBut *but)
void ui_button_execute_begin(struct bContext *UNUSED(C), struct ARegion *ar, uiBut *but, void **active_back)
{
- /* note: ideally we would not have to change 'but->active' howevwer
+ /* note: ideally we would not have to change 'but->active' however
* some functions we call don't use data (as they should be doing) */
uiHandleButtonData *data;
*active_back = but->active;
@@ -7750,6 +7772,8 @@ static int ui_handle_menu_event(
sub_v2_v2v2_int(mdiff, &event->x, menu->grab_xy_prev);
copy_v2_v2_int(menu->grab_xy_prev, &event->x);
+ add_v2_v2v2_int(menu->popup_create_vars.event_xy, menu->popup_create_vars.event_xy, mdiff);
+
ui_popup_translate(C, ar, mdiff);
}
@@ -8358,12 +8382,24 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE
but = ui_but_find_activated(ar);
if (but) {
+ uiBut *but_other;
uiHandleButtonData *data;
/* handle activated button events */
data = but->active;
- if (data->state == BUTTON_STATE_MENU_OPEN) {
+ if ((data->state == BUTTON_STATE_MENU_OPEN) &&
+ (but->type == PULLDOWN) &&
+ (but_other = ui_but_find_mouse_over(ar, event)) &&
+ (but != but_other) &&
+ (but->type == but_other->type))
+ {
+ /* if mouse moves to a different root-level menu button,
+ * open it to replace the current menu */
+ ui_handle_button_activate(C, ar, but_other, BUTTON_ACTIVATE_OVER);
+ button_activate_state(C, but_other, BUTTON_STATE_MENU_OPEN);
+ }
+ else if (data->state == BUTTON_STATE_MENU_OPEN) {
int retval;
/* handle events for menus and their buttons recursively,
@@ -8402,10 +8438,13 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE
static int ui_handler_popup(bContext *C, const wmEvent *event, void *userdata)
{
uiPopupBlockHandle *menu = userdata;
-
+ struct ARegion *menu_region;
/* we block all events, this is modal interaction, except for drop events which is described below */
int retval = WM_UI_HANDLER_BREAK;
+ menu_region = CTX_wm_menu(C);
+ CTX_wm_menu_set(C, menu->region);
+
if (event->type == EVT_DROP) {
/* if we're handling drop event we'll want it to be handled by popup callee as well,
* so it'll be possible to perform such operations as opening .blend files by dropping
@@ -8453,6 +8492,8 @@ static int ui_handler_popup(bContext *C, const wmEvent *event, void *userdata)
/* delayed apply callbacks */
ui_apply_but_funcs_after(C);
+ CTX_wm_region_set(C, menu_region);
+
return retval;
}
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 3fe6452e56b..cd3b6390184 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -428,6 +428,19 @@ struct uiKeyNavLock {
int event_xy[2];
};
+typedef uiBlock * (*uiBlockHandleCreateFunc)(struct bContext *C, struct uiPopupBlockHandle *handle, void *arg1);
+
+struct uiPopupBlockCreate {
+ uiBlockCreateFunc create_func;
+ uiBlockHandleCreateFunc handle_create_func;
+ void *arg;
+
+ int event_xy[2];
+
+ /* when popup is initialized from a button */
+ ARegion *butregion;
+};
+
struct uiPopupBlockHandle {
/* internal */
struct ARegion *region;
@@ -442,6 +455,9 @@ struct uiPopupBlockHandle {
void (*cancel_func)(struct bContext *C, void *arg);
void *popup_arg;
+ /* store data for refreshing popups */
+ struct uiPopupBlockCreate popup_create_vars;
+
struct wmTimer *scrolltimer;
struct uiKeyNavLock keynav_state;
@@ -455,7 +471,7 @@ struct uiPopupBlockHandle {
/* return values */
int butretval;
int menuretval;
- float retvalue;
+ int retvalue;
float retvec[4];
/* menu direction */
@@ -496,7 +512,8 @@ bool ui_searchbox_apply(uiBut *but, struct ARegion *ar);
void ui_searchbox_free(struct bContext *C, struct ARegion *ar);
void ui_but_search_test(uiBut *but);
-typedef uiBlock * (*uiBlockHandleCreateFunc)(struct bContext *C, struct uiPopupBlockHandle *handle, void *arg1);
+uiBlock *ui_popup_block_refresh(struct bContext *C, uiPopupBlockHandle *handle,
+ ARegion *butregion, uiBut *but);
uiPopupBlockHandle *ui_popup_block_create(struct bContext *C, struct ARegion *butregion, uiBut *but,
uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func,
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 0bc679dede0..789c10d6693 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -1759,7 +1759,7 @@ void uiItemV(uiLayout *layout, const char *name, int icon, int argval)
{
/* label */
uiBlock *block = layout->root->block;
- float *retvalue = (block->handle) ? &block->handle->retvalue : NULL;
+ int *retvalue = (block->handle) ? &block->handle->retvalue : NULL;
int w;
uiBlockSetCurLayout(block, layout);
@@ -1772,11 +1772,11 @@ void uiItemV(uiLayout *layout, const char *name, int icon, int argval)
w = ui_text_icon_width(layout, name, icon, 0);
if (icon && name[0])
- uiDefIconTextButF(block, BUT, argval, icon, name, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, -1, "");
+ uiDefIconTextButI(block, BUT, argval, icon, name, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, -1, "");
else if (icon)
- uiDefIconButF(block, BUT, argval, icon, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, -1, "");
+ uiDefIconButI(block, BUT, argval, icon, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, -1, "");
else
- uiDefButF(block, BUT, argval, name, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, -1, "");
+ uiDefButI(block, BUT, argval, name, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, -1, "");
}
/* separator item */
@@ -2983,6 +2983,8 @@ void uiBlockLayoutResolve(uiBlock *block, int *x, int *y)
{
uiLayoutRoot *root;
+ BLI_assert(block->active);
+
if (x) *x = 0;
if (y) *y = 0;
diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c
index 16f42f048e0..a3c3fb5e733 100644
--- a/source/blender/editors/interface/interface_regions.c
+++ b/source/blender/editors/interface/interface_regions.c
@@ -146,25 +146,40 @@ static void ui_remove_temporary_region(bContext *C, bScreen *sc, ARegion *ar)
/************************* Creating Tooltips **********************/
-typedef enum {
- UI_TIP_LC_MAIN,
- UI_TIP_LC_NORMAL,
- UI_TIP_LC_PYTHON,
- UI_TIP_LC_ALERT,
- UI_TIP_LC_SUBMENU
-} uiTooltipLineColor;
-#define UI_TIP_LC_MAX 5
+#define UI_TIP_PAD_FAC 1.3f
+#define UI_TIP_PADDING (int)(UI_TIP_PAD_FAC * UI_UNIT_Y)
#define MAX_TOOLTIP_LINES 8
typedef struct uiTooltipData {
rcti bbox;
uiFontStyle fstyle;
char lines[MAX_TOOLTIP_LINES][512];
- uiTooltipLineColor color_id[MAX_TOOLTIP_LINES];
+ char header[512], active_info[512];
+ struct {
+ enum {
+ UI_TIP_STYLE_NORMAL = 0,
+ UI_TIP_STYLE_HEADER,
+ UI_TIP_STYLE_MONO,
+ } style : 2;
+ enum {
+ UI_TIP_LC_MAIN = 0, /* primary text */
+ UI_TIP_LC_VALUE, /* the value of buttons (also shortcuts) */
+ UI_TIP_LC_ACTIVE, /* titles of active enum values */
+ UI_TIP_LC_NORMAL, /* regular text */
+ UI_TIP_LC_PYTHON, /* Python snippet */
+ UI_TIP_LC_ALERT, /* description of why operator can't run */
+ } color_id : 4;
+ int is_pad : 1;
+ } format[MAX_TOOLTIP_LINES];
int totline;
- int toth, spaceh, lineh;
+ int toth, lineh;
} uiTooltipData;
+#define UI_TIP_LC_MAX 6
+
+BLI_STATIC_ASSERT(UI_TIP_LC_MAX == UI_TIP_LC_ALERT + 1, "invalid lc-max");
+BLI_STATIC_ASSERT(sizeof(((uiTooltipData *)NULL)->format[0]) <= sizeof(int), "oversize");
+
static void rgb_tint(float col[3],
float h, float h_strength,
float v, float v_strength)
@@ -183,16 +198,18 @@ static void rgb_tint(float col[3],
static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
{
+ const float pad_px = UI_TIP_PADDING;
uiTooltipData *data = ar->regiondata;
uiWidgetColors *theme = ui_tooltip_get_theme();
rcti bbox = data->bbox;
float tip_colors[UI_TIP_LC_MAX][3];
float *main_color = tip_colors[UI_TIP_LC_MAIN]; /* the color from the theme */
+ float *value_color = tip_colors[UI_TIP_LC_VALUE];
+ float *active_color = tip_colors[UI_TIP_LC_ACTIVE];
float *normal_color = tip_colors[UI_TIP_LC_NORMAL];
float *python_color = tip_colors[UI_TIP_LC_PYTHON];
float *alert_color = tip_colors[UI_TIP_LC_ALERT];
- float *submenu_color = tip_colors[UI_TIP_LC_SUBMENU];
float background_color[3];
float tone_bg;
@@ -211,32 +228,78 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
/* calculate normal_color */
rgb_uchar_to_float(main_color, (const unsigned char *)theme->text);
+ copy_v3_v3(active_color, main_color);
copy_v3_v3(normal_color, main_color);
copy_v3_v3(python_color, main_color);
copy_v3_v3(alert_color, main_color);
- copy_v3_v3(submenu_color, main_color);
+ copy_v3_v3(value_color, main_color);
/* find the brightness difference between background and text colors */
tone_bg = rgb_to_grayscale(background_color);
/* tone_fg = rgb_to_grayscale(main_color); */
- rgb_tint(normal_color, 0.0f, 0.0f, tone_bg, 0.3f); /* a shade darker (to bg) */
- rgb_tint(python_color, 0.666f, 0.25f, tone_bg, 0.3f); /* blue */
- rgb_tint(alert_color, 0.0f, 0.8f, tone_bg, 0.1f); /* bright red */
- rgb_tint(submenu_color, 0.0f, 0.0f, tone_bg, 0.3f); /* a shade darker (to bg) */
+ /* mix the colors */
+ rgb_tint(value_color, 0.0f, 0.0f, tone_bg, 0.2f); /* light grey */
+ rgb_tint(active_color, 0.6f, 0.2f, tone_bg, 0.2f); /* light blue */
+ rgb_tint(normal_color, 0.0f, 0.0f, tone_bg, 0.4f); /* grey */
+ rgb_tint(python_color, 0.0f, 0.0f, tone_bg, 0.5f); /* dark grey */
+ rgb_tint(alert_color, 0.0f, 0.8f, tone_bg, 0.1f); /* red */
/* draw text */
- uiStyleFontSet(&data->fstyle);
- bbox.ymax = bbox.ymax - 0.5f * (BLI_rcti_size_y(&bbox) - data->toth);
+ bbox.xmin += 0.5f * pad_px; /* add padding to the text */
+ bbox.ymax -= 0.5f * (BLI_rcti_size_y(&bbox) - data->toth);
bbox.ymin = bbox.ymax - data->lineh;
for (i = 0; i < data->totline; i++) {
- glColor3fv(tip_colors[data->color_id[i]]);
- uiStyleFontDraw(&data->fstyle, &bbox, data->lines[i]);
- bbox.ymin -= data->lineh + data->spaceh;
- bbox.ymax -= data->lineh + data->spaceh;
+ if (data->format[i].style == UI_TIP_STYLE_HEADER) {
+ /* draw header and active data (is done here to be able to change color) */
+ uiFontStyle fstyle_header = data->fstyle;
+ float xofs;
+
+ /* override text-style */
+ fstyle_header.shadow = 1;
+ fstyle_header.shadowcolor = rgb_to_luma(tip_colors[UI_TIP_LC_MAIN]);
+ fstyle_header.shadowalpha = 1.0f;
+
+ uiStyleFontSet(&fstyle_header);
+ glColor3fv(tip_colors[UI_TIP_LC_MAIN]);
+ uiStyleFontDraw(&fstyle_header, &bbox, data->header);
+
+ xofs = BLF_width(fstyle_header.uifont_id, data->header, sizeof(data->header));
+ bbox.xmin += xofs;
+
+ glColor3fv(tip_colors[UI_TIP_LC_ACTIVE]);
+ uiStyleFontDraw(&data->fstyle, &bbox, data->active_info);
+
+ bbox.xmin -= xofs;
+ }
+ else if (data->format[i].style == UI_TIP_STYLE_MONO) {
+ uiFontStyle fstyle_mono = data->fstyle;
+ fstyle_mono.uifont_id = blf_mono_font;
+
+ uiStyleFontSet(&fstyle_mono);
+ /* XXX, needed because we dont have mono in 'U.uifonts' */
+ BLF_size(fstyle_mono.uifont_id, fstyle_mono.points * U.pixelsize, U.dpi);
+ glColor3fv(tip_colors[data->format[i].color_id]);
+ uiStyleFontDraw(&fstyle_mono, &bbox, data->lines[i]);
+ }
+ else {
+ BLI_assert(data->format[i].style == UI_TIP_STYLE_NORMAL);
+ /* draw remaining data */
+ uiStyleFontSet(&data->fstyle);
+ glColor3fv(tip_colors[data->format[i].color_id]);
+ uiStyleFontDraw(&data->fstyle, &bbox, data->lines[i]);
+ }
+ if ((i + 1 != data->totline) && data->format[i + 1].is_pad) {
+ bbox.ymax -= data->lineh * UI_TIP_PAD_FAC;
+ bbox.ymin -= data->lineh * UI_TIP_PAD_FAC;
+ }
+ else {
+ bbox.ymax -= data->lineh;
+ bbox.ymin -= data->lineh;
+ }
}
if (multisample_enabled)
@@ -254,6 +317,7 @@ static void ui_tooltip_region_free_cb(ARegion *ar)
ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
{
+ const float pad_px = UI_TIP_PADDING;
wmWindow *win = CTX_wm_window(C);
uiStyle *style = UI_GetStyle();
static ARegionType type;
@@ -264,7 +328,7 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
/* aspect values that shrink text are likely unreadable */
const float aspect = min_ff(1.0f, but->block->aspect);
float fonth, fontw;
- int winx /*, winy */, ofsx, ofsy, w, h, a;
+ int winx, ofsx, ofsy, w = 0, h, i;
rctf rect_fl;
rcti rect_i;
@@ -284,41 +348,46 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
uiButGetStrInfo(C, but, &but_tip, &enum_label, &enum_tip, &op_keymap, &prop_keymap, &rna_struct, &rna_prop, NULL);
- /* special case, enum rna buttons only have enum item description,
- * use general enum description too before the specific one */
-
/* Tip */
if (but_tip.strinfo) {
- /* Expanded Bit-flag enums have a specific way to select multiple... */
- if ((but->type & ROW) && but->rnaprop && RNA_property_flag(but->rnaprop) & PROP_ENUM_FLAG) {
- BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]),
- "%s %s", but_tip.strinfo, IFACE_("(Shift-click to select multiple)"));
- }
- else {
- BLI_strncpy(data->lines[data->totline], but_tip.strinfo, sizeof(data->lines[0]));
+ BLI_strncpy(data->header, but_tip.strinfo, sizeof(data->lines[0]));
+ if (enum_label.strinfo) {
+ BLI_snprintf(data->header, sizeof(data->header), "%s: ", but_tip.strinfo);
+ BLI_strncpy(data->active_info, enum_label.strinfo, sizeof(data->lines[0]));
}
- data->color_id[data->totline] = UI_TIP_LC_MAIN;
+ data->format[data->totline].style = UI_TIP_STYLE_HEADER;
data->totline++;
+
+ /* special case enum rna buttons */
+ if ((but->type & ROW) && but->rnaprop && RNA_property_flag(but->rnaprop) & PROP_ENUM_FLAG) {
+ BLI_strncpy(data->lines[data->totline], IFACE_("(Shift-click to select multiple)"), sizeof(data->lines[0]));
+
+ data->format[data->totline].color_id = UI_TIP_LC_NORMAL;
+ data->totline++;
+ }
+
}
/* Enum item label & tip */
- if (enum_label.strinfo && enum_tip.strinfo) {
- BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]),
- "%s: %s", enum_label.strinfo, enum_tip.strinfo);
- data->color_id[data->totline] = UI_TIP_LC_SUBMENU;
+ if (enum_tip.strinfo) {
+ BLI_strncpy(data->lines[data->totline], enum_tip.strinfo, sizeof(data->lines[0]));
+ data->format[data->totline].is_pad = true;
+ data->format[data->totline].color_id = UI_TIP_LC_VALUE;
data->totline++;
}
/* Op shortcut */
if (op_keymap.strinfo) {
BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Shortcut: %s"), op_keymap.strinfo);
- data->color_id[data->totline] = UI_TIP_LC_NORMAL;
+ data->format[data->totline].is_pad = true;
+ data->format[data->totline].color_id = UI_TIP_LC_VALUE;
data->totline++;
}
/* Property context-toggle shortcut */
if (prop_keymap.strinfo) {
BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Shortcut: %s"), prop_keymap.strinfo);
- data->color_id[data->totline] = UI_TIP_LC_NORMAL;
+ data->format[data->totline].is_pad = true;
+ data->format[data->totline].color_id = UI_TIP_LC_VALUE;
data->totline++;
}
@@ -329,7 +398,8 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
ui_get_but_string(but, buf, sizeof(buf));
if (buf[0]) {
BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Value: %s"), buf);
- data->color_id[data->totline] = UI_TIP_LC_NORMAL;
+ data->format[data->totline].is_pad = true;
+ data->format[data->totline].color_id = UI_TIP_LC_VALUE;
data->totline++;
}
}
@@ -344,7 +414,7 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
RNA_property_float_get_index(&but->rnapoin, but->rnaprop, but->rnaindex) :
RNA_property_float_get(&but->rnapoin, but->rnaprop);
BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Radians: %f"), value);
- data->color_id[data->totline] = UI_TIP_LC_NORMAL;
+ data->format[data->totline].color_id = UI_TIP_LC_NORMAL;
data->totline++;
}
}
@@ -353,7 +423,7 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
if (ui_but_anim_expression_get(but, buf, sizeof(buf))) {
/* expression */
BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Expression: %s"), buf);
- data->color_id[data->totline] = UI_TIP_LC_NORMAL;
+ data->format[data->totline].color_id = UI_TIP_LC_NORMAL;
data->totline++;
}
}
@@ -362,7 +432,7 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
ID *id = but->rnapoin.id.data;
if (id->lib) {
BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Library: %s"), id->lib->name);
- data->color_id[data->totline] = UI_TIP_LC_NORMAL;
+ data->format[data->totline].color_id = UI_TIP_LC_NORMAL;
data->totline++;
}
}
@@ -383,7 +453,9 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
/* operator info */
if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) {
BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Python: %s"), str);
- data->color_id[data->totline] = UI_TIP_LC_PYTHON;
+ data->format[data->totline].style = UI_TIP_STYLE_MONO;
+ data->format[data->totline].is_pad = true;
+ data->format[data->totline].color_id = UI_TIP_LC_PYTHON;
data->totline++;
}
@@ -397,7 +469,7 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
poll_msg = CTX_wm_operator_poll_msg_get(C);
if (poll_msg) {
BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Disabled: %s"), poll_msg);
- data->color_id[data->totline] = UI_TIP_LC_ALERT; /* alert */
+ data->format[data->totline].color_id = UI_TIP_LC_ALERT;
data->totline++;
}
}
@@ -414,7 +486,9 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]),
TIP_("Python: %s"), rna_struct.strinfo);
}
- data->color_id[data->totline] = UI_TIP_LC_PYTHON;
+ data->format[data->totline].style = UI_TIP_STYLE_MONO;
+ data->format[data->totline].is_pad = true;
+ data->format[data->totline].color_id = UI_TIP_LC_PYTHON;
data->totline++;
if (but->rnapoin.id.data) {
@@ -447,7 +521,8 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
}
MEM_freeN(id_path);
- data->color_id[data->totline] = UI_TIP_LC_PYTHON;
+ data->format[data->totline].style = UI_TIP_STYLE_MONO;
+ data->format[data->totline].color_id = UI_TIP_LC_PYTHON;
data->totline++;
}
}
@@ -486,22 +561,40 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
/* set font, get bb */
data->fstyle = style->widget; /* copy struct */
- data->fstyle.align = UI_STYLE_TEXT_CENTER;
ui_fontscale(&data->fstyle.points, aspect);
uiStyleFontSet(&data->fstyle);
/* these defines tweaked depending on font */
-#define TIP_MARGIN_Y (2.0f / aspect)
#define TIP_BORDER_X (16.0f / aspect)
#define TIP_BORDER_Y (6.0f / aspect)
h = BLF_height_max(data->fstyle.uifont_id);
- for (a = 0, fontw = 0, fonth = 0; a < data->totline; a++) {
- w = BLF_width(data->fstyle.uifont_id, data->lines[a], sizeof(data->lines[a]));
+ for (i = 0, fontw = 0, fonth = 0; i < data->totline; i++) {
+ if (data->format[i].style == UI_TIP_STYLE_HEADER) {
+ w = BLF_width(data->fstyle.uifont_id, data->header, sizeof(data->header));
+ if (enum_label.strinfo)
+ w += BLF_width(data->fstyle.uifont_id, data->active_info, sizeof(data->active_info));
+ }
+ else if (data->format[i].style == UI_TIP_STYLE_MONO) {
+ BLF_size(blf_mono_font, data->fstyle.points * U.pixelsize, U.dpi);
+
+ w = BLF_width(blf_mono_font, data->lines[i], sizeof(data->lines[i]));
+ }
+ else {
+ BLI_assert(data->format[i].style == UI_TIP_STYLE_NORMAL);
+ w = BLF_width(data->fstyle.uifont_id, data->lines[i], sizeof(data->lines[i]));
+ }
+
fontw = max_ff(fontw, (float)w);
- fonth += (a == 0) ? h : h + TIP_MARGIN_Y;
+
+ if ((i + 1 != data->totline) && data->format[i + 1].is_pad) {
+ fonth += h * UI_TIP_PAD_FAC;
+ }
+ else {
+ fonth += h;
+ }
}
//fontw *= aspect;
@@ -510,7 +603,6 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
data->toth = fonth;
data->lineh = h;
- data->spaceh = TIP_MARGIN_Y;
/* compute position */
ofsx = 0; //(but->block->panel) ? but->block->panel->ofsx : 0;
@@ -521,7 +613,6 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
rect_fl.ymax = but->rect.ymin + ofsy - TIP_BORDER_Y;
rect_fl.ymin = rect_fl.ymax - fonth - TIP_BORDER_Y;
-#undef TIP_MARGIN_Y
#undef TIP_BORDER_X
#undef TIP_BORDER_Y
@@ -555,20 +646,25 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
rect_i.ymin = 30;
}
+ /* add padding */
+ BLI_rcti_resize(&rect_i,
+ BLI_rcti_size_x(&rect_i) + pad_px,
+ BLI_rcti_size_y(&rect_i) + pad_px);
+
/* widget rect, in region coords */
{
int width = UI_ThemeMenuShadowWidth();
data->bbox.xmin = width;
- data->bbox.xmax = BLI_rcti_size_x(&rect_i) + width;
+ data->bbox.xmax = BLI_rcti_size_x(&rect_i) - width;
data->bbox.ymin = width;
- data->bbox.ymax = BLI_rcti_size_y(&rect_i) + width;
+ data->bbox.ymax = BLI_rcti_size_y(&rect_i);
/* region bigger for shadow */
ar->winrct.xmin = rect_i.xmin - width;
ar->winrct.xmax = rect_i.xmax + width;
ar->winrct.ymin = rect_i.ymin - width;
- ar->winrct.ymax = rect_i.ymax + MENU_TOP;
+ ar->winrct.ymax = rect_i.ymax + width;
}
/* adds subwindow */
@@ -1421,6 +1517,15 @@ static void ui_block_region_draw(const bContext *C, ARegion *ar)
{
uiBlock *block;
+ if (ar->do_draw & RGN_DRAW_REFRESH_UI) {
+ uiBlock *block_next;
+ ar->do_draw &= ~RGN_DRAW_REFRESH_UI;
+ for (block = ar->uiblocks.first; block; block = block_next) {
+ block_next = block->next;
+ ui_popup_block_refresh((bContext *)C, block->handle, NULL, NULL);
+ }
+ }
+
for (block = ar->uiblocks.first; block; block = block->next)
uiDrawBlock(C, block);
}
@@ -1502,42 +1607,50 @@ void ui_popup_block_scrolltest(uiBlock *block)
}
}
-uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut *but,
- uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func,
- void *arg)
+static void ui_popup_block_remove(bContext *C, uiPopupBlockHandle *handle)
{
- wmWindow *window = CTX_wm_window(C);
- static ARegionType type;
- ARegion *ar;
- uiBlock *block;
- uiPopupBlockHandle *handle;
- uiSafetyRct *saferct;
- int width = UI_ThemeMenuShadowWidth();
+ ui_remove_temporary_region(C, CTX_wm_screen(C), handle->region);
- /* create handle */
- handle = MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
+ if (handle->scrolltimer)
+ WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), handle->scrolltimer);
+}
- /* store context for operator */
- handle->ctx_area = CTX_wm_area(C);
- handle->ctx_region = CTX_wm_region(C);
-
- /* create area region */
- ar = ui_add_temporary_region(CTX_wm_screen(C));
- handle->region = ar;
+/**
+ * Called for creatign new popups and refreshing existing ones.
+ */
+uiBlock *ui_popup_block_refresh(
+ bContext *C, uiPopupBlockHandle *handle,
+ ARegion *butregion, uiBut *but)
+{
+ const int width = UI_ThemeMenuShadowWidth();
+ wmWindow *window = CTX_wm_window(C);
+ ARegion *ar = handle->region;
- memset(&type, 0, sizeof(ARegionType));
- type.draw = ui_block_region_draw;
- type.regionid = RGN_TYPE_TEMPORARY;
- ar->type = &type;
+ uiBlockCreateFunc create_func = handle->popup_create_vars.create_func;
+ uiBlockHandleCreateFunc handle_create_func = handle->popup_create_vars.handle_create_func;
+ void *arg = handle->popup_create_vars.arg;
- UI_add_region_handlers(&ar->handlers);
+ uiBlock *block_old = ar->uiblocks.first;
+ uiBlock *block;
+
+#ifdef DEBUG
+ wmEvent *event_back = window->eventstate;
+#endif
/* create ui block */
if (create_func)
- block = create_func(C, handle->region, arg);
+ block = create_func(C, ar, arg);
else
block = handle_create_func(C, handle, arg);
+ /* callbacks _must_ leave this for us, otherwise we can't call uiBlockUpdateFromOld */
+ BLI_assert(!block->endblock);
+
+ /* ensure we don't use mouse coords here! */
+#ifdef DEBUG
+ window->eventstate = NULL;
+#endif
+
if (block->handle) {
memcpy(block->handle, handle, sizeof(uiPopupBlockHandle));
MEM_freeN(handle);
@@ -1560,8 +1673,11 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut
block->flag |= UI_BLOCK_LOOP;
+ /* defer this until blocks are translated (below) */
+ block->oldblock = NULL;
+
if (!block->endblock)
- uiEndBlock(C, block);
+ uiEndBlock_ex(C, block, handle->popup_create_vars.event_xy);
/* if this is being created from a button */
if (but) {
@@ -1570,6 +1686,7 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut
handle->direction = block->direction;
}
else {
+ uiSafetyRct *saferct;
/* keep a list of these, needed for pulldown menus */
saferct = MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct");
saferct->safety = block->safety;
@@ -1589,12 +1706,18 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut
ui_block_translate(block, -ar->winrct.xmin, -ar->winrct.ymin);
- /* adds subwindow */
- ED_region_init(C, ar);
+ if (block_old) {
+ block->oldblock = block_old;
+ uiBlockUpdateFromOld(C, block);
+ uiFreeInactiveBlocks(C, &ar->uiblocks);
+ }
/* checks which buttons are visible, sets flags to prevent draw (do after region init) */
ui_popup_block_scrolltest(block);
+ /* adds subwindow */
+ ED_region_init(C, ar);
+
/* get winmat now that we actually have the subwindow */
wmSubWindowSet(window, ar->swinid);
@@ -1603,15 +1726,59 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut
/* notify change and redraw */
ED_region_tag_redraw(ar);
+ ED_region_update_rect(C, ar);
+
+#ifdef DEBUG
+ window->eventstate = event_back;
+#endif
+
+ return block;
+}
+
+uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut *but,
+ uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func,
+ void *arg)
+{
+ wmWindow *window = CTX_wm_window(C);
+ static ARegionType type;
+ ARegion *ar;
+ uiBlock *block;
+ uiPopupBlockHandle *handle;
+
+ /* create handle */
+ handle = MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
+
+ /* store context for operator */
+ handle->ctx_area = CTX_wm_area(C);
+ handle->ctx_region = CTX_wm_region(C);
+
+ /* store vars to refresh popup (RGN_DRAW_REFRESH_UI) */
+ handle->popup_create_vars.create_func = create_func;
+ handle->popup_create_vars.handle_create_func = handle_create_func;
+ handle->popup_create_vars.arg = arg;
+ handle->popup_create_vars.butregion = but ? butregion : NULL;
+ copy_v2_v2_int(handle->popup_create_vars.event_xy, &window->eventstate->x);
+
+ /* create area region */
+ ar = ui_add_temporary_region(CTX_wm_screen(C));
+ handle->region = ar;
+
+ memset(&type, 0, sizeof(ARegionType));
+ type.draw = ui_block_region_draw;
+ type.regionid = RGN_TYPE_TEMPORARY;
+ ar->type = &type;
+
+ UI_add_region_handlers(&ar->handlers);
+
+ block = ui_popup_block_refresh(C, handle, butregion, but);
+ handle = block->handle;
+
return handle;
}
void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle)
{
- ui_remove_temporary_region(C, CTX_wm_screen(C), handle->region);
-
- if (handle->scrolltimer)
- WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), handle->scrolltimer);
+ ui_popup_block_remove(C, handle);
MEM_freeN(handle);
}
@@ -2278,8 +2445,6 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi
if (pup->slideout)
uiBlockSetDirection(block, UI_RIGHT);
- uiEndBlock(C, block);
-
return pup->block;
}
diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c
index fa31c20eb74..c27789c0fc9 100644
--- a/source/blender/editors/interface/interface_style.c
+++ b/source/blender/editors/interface/interface_style.c
@@ -330,7 +330,7 @@ void UI_DrawString(float x, float y, const char *str)
/* reading without uifont will create one */
void uiStyleInit(void)
{
- uiFont *font = U.uifonts.first;
+ uiFont *font;
uiStyle *style = U.uistyles.first;
int monofont_size = datatoc_bmonofont_ttf_size;
unsigned char *monofont_ttf = (unsigned char *)datatoc_bmonofont_ttf;
@@ -340,11 +340,23 @@ void uiStyleInit(void)
U.dpi = 72;
CLAMP(U.dpi, 48, 144);
+ for (font = U.uifonts.first; font; font = font->next) {
+ BLF_unload_id(font->blf_id);
+ }
+
+ font = U.uifonts.first;
+
/* default builtin */
if (font == NULL) {
font = MEM_callocN(sizeof(uiFont), "ui font");
BLI_addtail(&U.uifonts, font);
-
+ }
+
+ if (U.font_path_ui[0]) {
+ BLI_strncpy(font->filename, U.font_path_ui, sizeof(font->filename));
+ font->uifont_id = UIFONT_CUSTOM1;
+ }
+ else {
BLI_strncpy(font->filename, "default", sizeof(font->filename));
font->uifont_id = UIFONT_DEFAULT;
}
@@ -381,8 +393,12 @@ void uiStyleInit(void)
}
else {
font->blf_id = BLF_load(font->filename);
- if (font->blf_id == -1)
+ if (font->blf_id == -1) {
font->blf_id = BLF_load_mem("default", (unsigned char *)datatoc_bfont_ttf, datatoc_bfont_ttf_size);
+ }
+ else {
+ BLF_default_set(font->blf_id);
+ }
}
if (font->blf_id == -1) {
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index dd951e48784..74977c2841e 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -209,7 +209,6 @@ static uiBlock *id_search_menu(bContext *C, ARegion *ar, void *arg_litem)
uiBoundsBlock(block, 0.3f * U.widget_unit);
uiBlockSetDirection(block, UI_DOWN);
- uiEndBlock(C, block);
/* give search-field focus */
uiButSetFocusOnEnter(win, but);
@@ -340,7 +339,7 @@ static const char *template_id_browse_tip(StructRNA *type)
case ID_LA: return N_("Browse Lamp Data to be linked");
case ID_CA: return N_("Browse Camera Data to be linked");
case ID_WO: return N_("Browse World Settings to be linked");
- case ID_SCR: return N_("Choose Screen lay-out");
+ case ID_SCR: return N_("Choose Screen layout");
case ID_TXT: return N_("Browse Text to be linked");
case ID_SPK: return N_("Browse Speaker Data to be linked");
case ID_SO: return N_("Browse Sound to be linked");
@@ -418,11 +417,9 @@ static void template_ID(bContext *C, uiLayout *layout, TemplateID *template, Str
but = uiDefBlockButN(block, id_search_menu, MEM_dupallocN(template), "", 0, 0, UI_UNIT_X * 6, UI_UNIT_Y * 6,
TIP_(template_id_browse_tip(type)));
- if (type) {
- but->icon = RNA_struct_ui_icon(type);
- if (id) but->icon = ui_id_icon_get(C, id, true);
- uiButSetFlag(but, UI_HAS_ICON | UI_ICON_PREVIEW);
- }
+ but->icon = id ? ui_id_icon_get(C, id, true) : RNA_struct_ui_icon(type);
+ uiButSetFlag(but, UI_HAS_ICON | UI_ICON_PREVIEW);
+
if ((idfrom && idfrom->lib) || !editable)
uiButSetFlag(but, UI_BUT_DISABLED);
@@ -431,14 +428,11 @@ static void template_ID(bContext *C, uiLayout *layout, TemplateID *template, Str
else if (flag & UI_ID_BROWSE) {
but = uiDefBlockButN(block, id_search_menu, MEM_dupallocN(template), "", 0, 0, UI_UNIT_X * 1.6, UI_UNIT_Y,
TIP_(template_id_browse_tip(type)));
-
- if (type) {
- but->icon = RNA_struct_ui_icon(type);
- /* default dragging of icon for id browse buttons */
- uiButSetDragID(but, id);
- uiButSetFlag(but, UI_HAS_ICON);
- uiButSetDrawFlag(but, UI_BUT_ICON_LEFT);
- }
+ but->icon = RNA_struct_ui_icon(type);
+ /* default dragging of icon for id browse buttons */
+ uiButSetDragID(but, id);
+ uiButSetFlag(but, UI_HAS_ICON);
+ uiButSetDrawFlag(but, UI_BUT_ICON_LEFT);
if ((idfrom && idfrom->lib) || !editable)
uiButSetFlag(but, UI_BUT_DISABLED);
@@ -1656,8 +1650,7 @@ static uiBlock *icon_view_menu(bContext *C, ARegion *ar, void *arg_litem)
uiBoundsBlock(block, 0.3f * U.widget_unit);
uiBlockSetDirection(block, UI_TOP);
- uiEndBlock(C, block);
-
+
if (free) {
MEM_freeN(item);
}
@@ -3552,8 +3545,7 @@ static uiBlock *component_menu(bContext *C, ARegion *ar, void *args_v)
uiItemR(layout, &args->ptr, args->propname, UI_ITEM_R_EXPAND, "", ICON_NONE);
uiBoundsBlock(block, 6);
- uiBlockSetDirection(block, UI_DOWN);
- uiEndBlock(C, block);
+ uiBlockSetDirection(block, UI_DOWN);
return block;
}
diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c
index 2c958c5028a..744ed7e5b72 100644
--- a/source/blender/editors/interface/interface_utils.c
+++ b/source/blender/editors/interface/interface_utils.c
@@ -253,7 +253,7 @@ int uiFloatPrecisionCalc(int prec, double value)
static const double max_pow = 10000000.0; /* pow(10, UI_PRECISION_FLOAT_MAX) */
BLI_assert(prec <= UI_PRECISION_FLOAT_MAX);
- BLI_assert(pow10_neg[prec] == pow(10, -prec));
+ BLI_assert(fabs(pow10_neg[prec] - pow(10, -prec)) < 1e-16);
/* check on the number of decimal places need to display the number, this is so 0.00001 is not displayed as 0.00,
* _but_, this is only for small values si 10.0001 will not get the same treatment.
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index aed492125cf..6ea0a55729d 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -477,13 +477,17 @@ static void round_box_edges(uiWidgetBase *wt, int roundboxalign, const rcti *rec
/* based on button rect, return scaled array of triangles */
-static void widget_num_tria(uiWidgetTrias *tria, const rcti *rect, float triasize, char where)
+static void widget_draw_tria_ex(
+ uiWidgetTrias *tria, const rcti *rect, float triasize, char where,
+ /* input data */
+ const float verts[][2], const int verts_tot,
+ const unsigned int tris[][3], const int tris_tot)
{
float centx, centy, sizex, sizey, minsize;
int a, i1 = 0, i2 = 1;
-
+
minsize = min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect));
-
+
/* center position and size */
centx = (float)rect->xmin + 0.5f * minsize;
centy = (float)rect->ymin + 0.5f * minsize;
@@ -502,49 +506,30 @@ static void widget_num_tria(uiWidgetTrias *tria, const rcti *rect, float triasiz
sizex = -sizex;
i2 = 0; i1 = 1;
}
-
- for (a = 0; a < 3; a++) {
- tria->vec[a][0] = sizex * num_tria_vert[a][i1] + centx;
- tria->vec[a][1] = sizey * num_tria_vert[a][i2] + centy;
+
+ for (a = 0; a < verts_tot; a++) {
+ tria->vec[a][0] = sizex * verts[a][i1] + centx;
+ tria->vec[a][1] = sizey * verts[a][i2] + centy;
}
-
- tria->tot = 1;
- tria->index = num_tria_face;
+
+ tria->tot = tris_tot;
+ tria->index = tris;
}
-static void widget_scroll_circle(uiWidgetTrias *tria, const rcti *rect, float triasize, char where)
+static void widget_num_tria(uiWidgetTrias *tria, const rcti *rect, float triasize, char where)
{
- float centx, centy, sizex, sizey, minsize;
- int a, i1 = 0, i2 = 1;
-
- minsize = min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect));
-
- /* center position and size */
- centx = (float)rect->xmin + 0.5f * minsize;
- centy = (float)rect->ymin + 0.5f * minsize;
- sizex = sizey = -0.5f * triasize * minsize;
+ widget_draw_tria_ex(
+ tria, rect, triasize, where,
+ num_tria_vert, ARRAY_SIZE(num_tria_vert),
+ num_tria_face, ARRAY_SIZE(num_tria_face));
+}
- if (where == 'r') {
- centx = (float)rect->xmax - 0.5f * minsize;
- sizex = -sizex;
- }
- else if (where == 't') {
- centy = (float)rect->ymax - 0.5f * minsize;
- sizey = -sizey;
- i2 = 0; i1 = 1;
- }
- else if (where == 'b') {
- sizex = -sizex;
- i2 = 0; i1 = 1;
- }
-
- for (a = 0; a < 16; a++) {
- tria->vec[a][0] = sizex * scroll_circle_vert[a][i1] + centx;
- tria->vec[a][1] = sizey * scroll_circle_vert[a][i2] + centy;
- }
-
- tria->tot = 14;
- tria->index = scroll_circle_face;
+static void widget_scroll_circle(uiWidgetTrias *tria, const rcti *rect, float triasize, char where)
+{
+ widget_draw_tria_ex(
+ tria, rect, triasize, where,
+ scroll_circle_vert, ARRAY_SIZE(scroll_circle_vert),
+ scroll_circle_face, ARRAY_SIZE(scroll_circle_face));
}
static void widget_trias_draw(uiWidgetTrias *tria)
@@ -1828,7 +1813,9 @@ static void widget_state(uiWidgetType *wt, int state)
if (state & UI_BUT_DRAG_MULTI) {
/* the button isn't SELECT but we're editing this so draw with sel color */
- widget_state_blend(wt->wcol.inner, wt->wcol.inner_sel, 1.0f);
+ copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel);
+ SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
+ widget_state_blend(wt->wcol.text, wt->wcol.text_sel, 0.85f);
}
if (state & UI_BUT_NODE_ACTIVE) {
diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c
index daf28bc4263..72f15697a5f 100644
--- a/source/blender/editors/interface/resources.c
+++ b/source/blender/editors/interface/resources.c
@@ -2420,6 +2420,13 @@ void init_userdef_do_versions(void)
}
}
+ if (U.versionfile < 272 || (U.versionfile == 272 && U.subversionfile < 1)) {
+ bTheme *btheme;
+ for (btheme = U.themes.first; btheme; btheme = btheme->next) {
+ rgba_char_args_set(btheme->tui.wcol_tooltip.text, 255, 255, 255, 255);
+ }
+ }
+
{
bTheme *btheme;
for (btheme = U.themes.first; btheme; btheme = btheme->next) {
diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c
index 982e8f1a9fe..45dd47097f5 100644
--- a/source/blender/editors/interface/view2d.c
+++ b/source/blender/editors/interface/view2d.c
@@ -2277,6 +2277,9 @@ typedef struct View2DString {
} col;
rcti rect;
int mval[2];
+
+ /* str is allocated past the end */
+ char str[0];
} View2DString;
/* assumes caches are used correctly, so for time being no local storage in v2d */
@@ -2309,7 +2312,7 @@ void UI_view2d_text_cache_add(View2D *v2d, float x, float y,
v2s->mval[0] = mval[0];
v2s->mval[1] = mval[1];
- memcpy(v2s + 1, str, alloc_len);
+ memcpy(v2s->str, str, alloc_len);
}
}
@@ -2340,7 +2343,7 @@ void UI_view2d_text_cache_add_rectf(View2D *v2d, const rctf *rect_view,
v2s->mval[0] = v2s->rect.xmin;
v2s->mval[1] = v2s->rect.ymin;
- memcpy(v2s + 1, str, alloc_len);
+ memcpy(v2s->str, str, alloc_len);
}
}
@@ -2360,7 +2363,6 @@ void UI_view2d_text_cache_draw(ARegion *ar)
ED_region_pixelspace(ar);
for (v2s = g_v2d_strings; v2s; v2s = v2s->next) {
- const char *str = (const char *)(v2s + 1);
int xofs = 0, yofs;
yofs = ceil(0.5f * (BLI_rcti_size_y(&v2s->rect) - default_height));
@@ -2372,11 +2374,13 @@ void UI_view2d_text_cache_draw(ARegion *ar)
}
if (v2s->rect.xmin >= v2s->rect.xmax)
- BLF_draw_default((float)v2s->mval[0] + xofs, (float)v2s->mval[1] + yofs, 0.0, str, BLF_DRAW_STR_DUMMY_MAX);
+ BLF_draw_default((float)(v2s->mval[0] + xofs), (float)(v2s->mval[1] + yofs), 0.0,
+ v2s->str, BLF_DRAW_STR_DUMMY_MAX);
else {
BLF_clipping_default(v2s->rect.xmin - 4, v2s->rect.ymin - 4, v2s->rect.xmax + 4, v2s->rect.ymax + 4);
BLF_enable_default(BLF_CLIPPING);
- BLF_draw_default(v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f, str, BLF_DRAW_STR_DUMMY_MAX);
+ BLF_draw_default(v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f,
+ v2s->str, BLF_DRAW_STR_DUMMY_MAX);
BLF_disable_default(BLF_CLIPPING);
}
}
diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c
index 5e84d9e6916..a882284f5e3 100644
--- a/source/blender/editors/mask/mask_ops.c
+++ b/source/blender/editors/mask/mask_ops.c
@@ -2077,6 +2077,8 @@ static int mask_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
point++;
}
if (end >= start) {
+ int tot_point;
+ int tot_point_shape_start;
MaskSpline *new_spline = BKE_mask_spline_add(mask_layer);
MaskSplinePoint *new_point;
int b;
@@ -2100,15 +2102,29 @@ static int mask_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
memcpy(new_spline->points, spline->points + start,
new_spline->tot_point * sizeof(MaskSplinePoint));
+ tot_point = new_spline->tot_point;
+
+ /* animation requires points added one by one */
+ if (mask_layer->splines_shapes.first) {
+ new_spline->tot_point = 0;
+ tot_point_shape_start = BKE_mask_layer_shape_spline_to_index(mask_layer, new_spline);
+ }
+
/* Select points and duplicate their UWs (if needed). */
for (b = 0, new_point = new_spline->points;
- b < new_spline->tot_point;
+ b < tot_point;
b++, new_point++)
{
if (new_point->uw) {
new_point->uw = MEM_dupallocN(new_point->uw);
}
BKE_mask_point_select_set(new_point, true);
+
+
+ if (mask_layer->splines_shapes.first) {
+ new_spline->tot_point++;
+ BKE_mask_layer_shape_changed_add(mask_layer, tot_point_shape_start + b, true, false);
+ }
}
/* Clear cyclic flag if we didn't copy the whole spline. */
diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c
index af6f127327c..a4268bddaf5 100644
--- a/source/blender/editors/mask/mask_select.c
+++ b/source/blender/editors/mask/mask_select.c
@@ -683,7 +683,7 @@ void MASK_OT_select_circle(wmOperatorType *ot)
/* properties */
RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
- RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "radius", 1, 1, INT_MAX, "Radius", "", 1, INT_MAX);
RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
}
diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt
index 2fe5dcc888c..a5e98b651a3 100644
--- a/source/blender/editors/mesh/CMakeLists.txt
+++ b/source/blender/editors/mesh/CMakeLists.txt
@@ -51,6 +51,7 @@ set(SRC
editmesh_loopcut.c
editmesh_path.c
editmesh_rip.c
+ editmesh_rip_edge.c
editmesh_select.c
editmesh_tools.c
editmesh_utils.c
diff --git a/source/blender/editors/mesh/editface.c b/source/blender/editors/mesh/editface.c
index 5150a703951..87b429f1165 100644
--- a/source/blender/editors/mesh/editface.c
+++ b/source/blender/editors/mesh/editface.c
@@ -204,7 +204,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const unsigned int index,
/* only put face under cursor in array */
mp = &me->mpoly[index];
BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart);
- BLI_BITMAP_SET(poly_tag, index);
+ BLI_BITMAP_ENABLE(poly_tag, index);
}
else {
/* fill array by selection */
@@ -215,7 +215,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const unsigned int index,
}
else if (mp->flag & ME_FACE_SEL) {
BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart);
- BLI_BITMAP_SET(poly_tag, a);
+ BLI_BITMAP_ENABLE(poly_tag, a);
}
}
}
@@ -229,13 +229,13 @@ static void select_linked_tfaces_with_seams(Mesh *me, const unsigned int index,
if (mp->flag & ME_HIDE)
continue;
- if (!BLI_BITMAP_GET(poly_tag, a)) {
+ if (!BLI_BITMAP_TEST(poly_tag, a)) {
mark = false;
ml = me->mloop + mp->loopstart;
for (b = 0; b < mp->totloop; b++, ml++) {
if ((me->medge[ml->e].flag & ME_SEAM) == 0) {
- if (BLI_BITMAP_GET(edge_tag, ml->e)) {
+ if (BLI_BITMAP_TEST(edge_tag, ml->e)) {
mark = true;
break;
}
@@ -243,7 +243,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const unsigned int index,
}
if (mark) {
- BLI_BITMAP_SET(poly_tag, a);
+ BLI_BITMAP_ENABLE(poly_tag, a);
BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart);
do_it = true;
}
@@ -254,7 +254,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const unsigned int index,
MEM_freeN(edge_tag);
for (a = 0, mp = me->mpoly; a < me->totpoly; a++, mp++) {
- if (BLI_BITMAP_GET(poly_tag, a)) {
+ if (BLI_BITMAP_TEST(poly_tag, a)) {
BKE_BIT_TEST_SET(mp->flag, select, ME_FACE_SEL);
}
}
diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c
index 75ba84aaffd..ec1ea3e8e62 100644
--- a/source/blender/editors/mesh/editmesh_bevel.c
+++ b/source/blender/editors/mesh/editmesh_bevel.c
@@ -400,8 +400,21 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_RUNNING_MODAL;
}
+static void mesh_ot_bevel_offset_range_func(PointerRNA *ptr, PropertyRNA *UNUSED(prop),
+ float *min, float *max, float *softmin, float *softmax)
+{
+ const int offset_type = RNA_enum_get(ptr, "offset_type");
+
+ *min = -FLT_MAX;
+ *max = FLT_MAX;
+ *softmin = 0.0f;
+ *softmax = (offset_type == BEVEL_AMT_PERCENT) ? 100.0f : 1.0f;
+}
+
void MESH_OT_bevel(wmOperatorType *ot)
{
+ PropertyRNA *prop;
+
static EnumPropertyItem offset_type_items[] = {
{BEVEL_AMT_OFFSET, "OFFSET", 0, "Offset", "Amount is offset of new edges from original"},
{BEVEL_AMT_WIDTH, "WIDTH", 0, "Width", "Amount is width of new face"},
@@ -426,7 +439,8 @@ void MESH_OT_bevel(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_POINTER | OPTYPE_BLOCKING;
RNA_def_enum(ot->srna, "offset_type", offset_type_items, 0, "Amount Type", "What distance Amount measures");
- RNA_def_float(ot->srna, "offset", 0.0f, -FLT_MAX, FLT_MAX, "Amount", "", 0.0f, 1.0f);
+ prop = RNA_def_float(ot->srna, "offset", 0.0f, -FLT_MAX, FLT_MAX, "Amount", "", 0.0f, 1.0f);
+ RNA_def_property_float_array_funcs_runtime(prop, NULL, NULL, mesh_ot_bevel_offset_range_func);
RNA_def_int(ot->srna, "segments", 1, 1, 50, "Segments", "Segments for curved edge", 1, 8);
RNA_def_float(ot->srna, "profile", 0.5f, 0.15f, 1.0f, "Profile", "Controls profile shape (0.5 = round)", 0.15f, 1.0f);
RNA_def_boolean(ot->srna, "vertex_only", false, "Vertex only", "Bevel only vertices");
diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c
index 5da33663897..595c43c1060 100644
--- a/source/blender/editors/mesh/editmesh_extrude.c
+++ b/source/blender/editors/mesh/editmesh_extrude.c
@@ -693,7 +693,7 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w
void MESH_OT_dupli_extrude_cursor(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Duplicate or Extrude at 3D Cursor";
+ ot->name = "Duplicate or Extrude to Cursor";
ot->idname = "MESH_OT_dupli_extrude_cursor";
ot->description = "Duplicate and extrude selected vertices, edges or faces towards the mouse cursor";
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index 0c84199f2c4..96a608a432b 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -1161,6 +1161,13 @@ static bool point_is_visible(KnifeTool_OpData *kcd, const float p[3], const floa
{
BMFace *f_hit;
+ /* If box clipping on, make sure p is not clipped */
+ if (kcd->vc.rv3d->rflag & RV3D_CLIPPING &&
+ ED_view3d_clipping_test(kcd->vc.rv3d, p, true))
+ {
+ return false;
+ }
+
/* If not cutting through, make sure no face is in front of p */
if (!kcd->cut_through) {
float dist;
@@ -1177,8 +1184,19 @@ static bool point_is_visible(KnifeTool_OpData *kcd, const float p[3], const floa
madd_v3_v3v3fl(p_ofs, p, view, KNIFE_FLT_EPSBIG * 3.0f);
/* avoid projecting behind the viewpoint */
- if (kcd->is_ortho) {
- dist = FLT_MAX;
+ if (kcd->is_ortho && (kcd->vc.rv3d->persp != RV3D_CAMOB)) {
+ dist = kcd->vc.v3d->far * 2.0f;
+ }
+
+ if (kcd->vc.rv3d->rflag & RV3D_CLIPPING) {
+ float view_clip[2][3];
+ /* note: view_clip[0] should never get clipped */
+ copy_v3_v3(view_clip[0], p_ofs);
+ madd_v3_v3v3fl(view_clip[1], p_ofs, view, dist);
+
+ if (clip_segment_v3_plane_n(view_clip[0], view_clip[1], kcd->vc.rv3d->clip_local, 6)) {
+ dist = len_v3v3(p_ofs, view_clip[1]);
+ }
}
/* see if there's a face hit between p1 and the view */
@@ -1187,13 +1205,6 @@ static bool point_is_visible(KnifeTool_OpData *kcd, const float p[3], const floa
return false;
}
- /* If box clipping on, make sure p is not clipped */
- if (kcd->vc.rv3d->rflag & RV3D_CLIPPING &&
- ED_view3d_clipping_test(kcd->vc.rv3d, p, true))
- {
- return false;
- }
-
return true;
}
@@ -1215,7 +1226,7 @@ static void set_linehit_depth(KnifeTool_OpData *kcd, KnifeLineHit *lh)
ED_view3d_win_to_segment(kcd->ar, kcd->vc.v3d, lh->schit, vnear, vfar, true);
mul_m4_v3(kcd->ob->imat, vnear);
- if (kcd->is_ortho) {
+ if (kcd->is_ortho && (kcd->vc.rv3d->persp != RV3D_CAMOB)) {
if (kcd->ortho_extent == 0.0f)
calc_ortho_extent(kcd);
clip_to_ortho_planes(vnear, vfar, kcd->ortho_extent + 10.0f);
@@ -1291,7 +1302,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
* this gives precision error; rather then solving properly
* (which may involve using doubles everywhere!),
* limit the distance between these points */
- if (kcd->is_ortho) {
+ if (kcd->is_ortho && (kcd->vc.rv3d->persp != RV3D_CAMOB)) {
if (kcd->ortho_extent == 0.0f)
calc_ortho_extent(kcd);
clip_to_ortho_planes(v1, v3, kcd->ortho_extent + 10.0f);
diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c
index 58701e776b7..3d3e41d31cc 100644
--- a/source/blender/editors/mesh/editmesh_rip.c
+++ b/source/blender/editors/mesh/editmesh_rip.c
@@ -795,8 +795,6 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve
BM_mesh_edgesplit(em->bm, true, true, true);
}
- dist_sq = FLT_MAX;
-
{
/* --- select which vert --- */
BMVert *v_best = NULL;
diff --git a/source/blender/editors/mesh/editmesh_rip_edge.c b/source/blender/editors/mesh/editmesh_rip_edge.c
new file mode 100644
index 00000000000..9ffdd715616
--- /dev/null
+++ b/source/blender/editors/mesh/editmesh_rip_edge.c
@@ -0,0 +1,244 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/mesh/editmesh_rip_edge.c
+ * \ingroup edmesh
+ *
+ * based on mouse cursor position, split of vertices along the closest edge.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_object_types.h"
+
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_report.h"
+#include "BKE_editmesh.h"
+
+#include "RNA_define.h"
+#include "RNA_access.h"
+
+#include "WM_types.h"
+
+#include "ED_mesh.h"
+#include "ED_screen.h"
+#include "ED_transform.h"
+#include "ED_view3d.h"
+
+#include "bmesh.h"
+
+#include "mesh_intern.h" /* own include */
+
+/* uses total number of selected edges around a vertex to choose how to extend */
+#define USE_TRICKY_EXTEND
+
+static int edbm_rip_edge_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+{
+ ARegion *ar = CTX_wm_region(C);
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ Object *obedit = CTX_data_edit_object(C);
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMesh *bm = em->bm;
+ BMIter viter;
+ BMVert *v;
+ const float mval_fl[2] = {UNPACK2(event->mval)};
+ float cent_sco[2];
+ int cent_tot;
+
+ /* mouse direction to view center */
+ float mval_dir[2];
+
+ float projectMat[4][4];
+
+ if (bm->totvertsel == 0)
+ return OPERATOR_CANCELLED;
+
+ ED_view3d_ob_project_mat_get(rv3d, obedit, projectMat);
+
+ zero_v2(cent_sco);
+ cent_tot = 0;
+
+ /* clear tags and calc screen center */
+ BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
+ BM_elem_flag_disable(v, BM_ELEM_TAG);
+
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+ float v_sco[2];
+ ED_view3d_project_float_v2_m4(ar, v->co, v_sco, projectMat);
+
+ add_v2_v2(cent_sco, v_sco);
+ cent_tot += 1;
+ }
+ }
+ mul_v2_fl(cent_sco, 1.0f / (float)cent_tot);
+
+ /* not essential, but gives more expected results with edge selection */
+ if (bm->totedgesel) {
+ /* angle against center can give odd result,
+ * try re-position the center to the closest edge */
+ BMIter eiter;
+ BMEdge *e;
+ float dist_sq_best = len_squared_v2v2(cent_sco, mval_fl);
+
+ BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
+ if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
+ float e_sco[2][2];
+ float cent_sco_test[2];
+ float dist_sq_test;
+
+ ED_view3d_project_float_v2_m4(ar, e->v1->co, e_sco[0], projectMat);
+ ED_view3d_project_float_v2_m4(ar, e->v2->co, e_sco[1], projectMat);
+
+ closest_to_line_segment_v2(cent_sco_test, mval_fl, e_sco[0], e_sco[1]);
+ dist_sq_test = len_squared_v2v2(cent_sco_test, mval_fl);
+ if (dist_sq_test < dist_sq_best) {
+ dist_sq_best = dist_sq_test;
+
+ /* we have a new screen center */
+ copy_v2_v2(cent_sco, cent_sco_test);
+ }
+ }
+ }
+ }
+
+ sub_v2_v2v2(mval_dir, mval_fl, cent_sco);
+ normalize_v2(mval_dir);
+
+ /* operate on selected verts */
+ BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
+ BMIter eiter;
+ BMEdge *e;
+ float v_sco[2];
+
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT) &&
+ BM_elem_flag_test(v, BM_ELEM_TAG) == false)
+ {
+ /* Rules for */
+ float angle_best = FLT_MAX;
+ BMEdge *e_best = NULL;
+
+#ifdef USE_TRICKY_EXTEND
+ /* first check if we can select the edge to split based on selection-only */
+ int tot_sel = 0, tot = 0;
+
+ BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
+ if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
+ if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
+ e_best = e;
+ tot_sel += 1;
+ }
+ tot += 1;
+ }
+ }
+ if (tot_sel != 1) {
+ e_best = NULL;
+ }
+
+ /* only one edge selected, operate on that */
+ if (e_best) {
+ goto found_edge;
+ }
+ /* none selected, fall through and find one */
+ else if (tot_sel == 0) {
+ /* pass */
+ }
+ /* selection not 0 or 1, do nothing */
+ else {
+ goto found_edge;
+ }
+#endif
+ ED_view3d_project_float_v2_m4(ar, v->co, v_sco, projectMat);
+
+ BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
+ if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
+ BMVert *v_other = BM_edge_other_vert(e, v);
+ float v_other_sco[2];
+ float angle_test;
+
+ ED_view3d_project_float_v2_m4(ar, v_other->co, v_other_sco, projectMat);
+
+ /* avoid comparing with view-axis aligned edges (less then a pixel) */
+ if (len_squared_v2v2(v_sco, v_other_sco) > 1.0f) {
+ float v_dir[2];
+
+ sub_v2_v2v2(v_dir, v_other_sco, v_sco);
+ normalize_v2(v_dir);
+
+ angle_test = angle_normalized_v2v2(mval_dir, v_dir);
+
+ if (angle_test < angle_best) {
+ angle_best = angle_test;
+ e_best = e;
+ }
+ }
+ }
+ }
+
+#ifdef USE_TRICKY_EXTEND
+found_edge:
+#endif
+ if (e_best) {
+ const bool e_select = BM_elem_flag_test_bool(e_best, BM_ELEM_SELECT);
+ BMVert *v_new;
+ BMEdge *e_new;
+
+ v_new = BM_edge_split(bm, e_best, v, &e_new, 0.0f);
+
+ BM_vert_select_set(bm, v, false);
+ BM_edge_select_set(bm, e_new, false);
+
+ BM_vert_select_set(bm, v_new, true);
+ if (e_select) {
+ BM_edge_select_set(bm, e_best, true);
+ }
+ BM_elem_flag_enable(v_new, BM_ELEM_TAG); /* prevent further splitting */
+ }
+ }
+ }
+
+ BM_select_history_clear(bm);
+
+ BM_mesh_select_mode_flush(bm);
+
+ EDBM_update_generic(em, true, true);
+
+ return OPERATOR_FINISHED;
+}
+
+
+void MESH_OT_rip_edge(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Extend Vertices";
+ ot->idname = "MESH_OT_rip_edge";
+ ot->description = "Extend vertices along the edge closest to the cursor";
+
+ /* api callbacks */
+ ot->invoke = edbm_rip_edge_invoke;
+ ot->poll = EDBM_view3d_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* to give to transform */
+ Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY);
+}
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index 467723e5e0a..23828098940 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -213,7 +213,7 @@ bool EDBM_backbuf_border_init(ViewContext *vc, short xmin, short ymin, short xma
a = (xmax - xmin + 1) * (ymax - ymin + 1);
while (a--) {
if (*dr > 0 && *dr <= bm_vertoffs) {
- BLI_BITMAP_SET(selbuf, *dr);
+ BLI_BITMAP_ENABLE(selbuf, *dr);
}
dr++;
}
@@ -230,7 +230,7 @@ bool EDBM_backbuf_check(unsigned int index)
return true;
if (index > 0 && index <= bm_vertoffs)
- return BLI_BITMAP_GET_BOOL(selbuf, index);
+ return BLI_BITMAP_TEST_BOOL(selbuf, index);
return false;
}
@@ -297,7 +297,7 @@ bool EDBM_backbuf_border_mask_init(ViewContext *vc, const int mcords[][2], short
a = (xmax - xmin + 1) * (ymax - ymin + 1);
while (a--) {
if (*dr > 0 && *dr <= bm_vertoffs && *dr_mask == true) {
- BLI_BITMAP_SET(selbuf, *dr);
+ BLI_BITMAP_ENABLE(selbuf, *dr);
}
dr++; dr_mask++;
}
@@ -340,7 +340,7 @@ bool EDBM_backbuf_circle_init(ViewContext *vc, short xs, short ys, short rads)
for (xc = -rads; xc <= rads; xc++, dr++) {
if (xc * xc + yc * yc < radsq) {
if (*dr > 0 && *dr <= bm_vertoffs) {
- BLI_BITMAP_SET(selbuf, *dr);
+ BLI_BITMAP_ENABLE(selbuf, *dr);
}
}
}
@@ -3189,7 +3189,7 @@ void MESH_OT_region_to_loop(wmOperatorType *ot)
}
static int loop_find_region(BMLoop *l, int flag,
- SmallHash *fhash, BMFace ***region_out)
+ GSet *visit_face_set, BMFace ***region_out)
{
BMFace **region = NULL;
BMFace **stack = NULL;
@@ -3198,7 +3198,7 @@ static int loop_find_region(BMLoop *l, int flag,
BMFace *f;
BLI_array_append(stack, l->f);
- BLI_smallhash_insert(fhash, (uintptr_t)l->f, NULL);
+ BLI_gset_insert(visit_face_set, l->f);
while (BLI_array_count(stack) > 0) {
BMIter liter1, liter2;
@@ -3217,11 +3217,10 @@ static int loop_find_region(BMLoop *l, int flag,
if (BM_elem_flag_test(l2->f, BM_ELEM_TAG)) {
continue;
}
- if (BLI_smallhash_haskey(fhash, (uintptr_t)l2->f))
- continue;
-
- BLI_array_append(stack, l2->f);
- BLI_smallhash_insert(fhash, (uintptr_t)l2->f, NULL);
+
+ if (BLI_gset_add(visit_face_set, l2->f)) {
+ BLI_array_append(stack, l2->f);
+ }
}
}
}
@@ -3254,13 +3253,13 @@ static int verg_radial(const void *va, const void *vb)
*/
static int loop_find_regions(BMEditMesh *em, const bool selbigger)
{
- SmallHash visithash;
+ GSet *visit_face_set;
BMIter iter;
const int edges_len = em->bm->totedgesel;
BMEdge *e, **edges;
int count = 0, i;
- BLI_smallhash_init_ex(&visithash, edges_len);
+ visit_face_set = BLI_gset_ptr_new_ex(__func__, edges_len);
edges = MEM_mallocN(sizeof(*edges) * edges_len, __func__);
i = 0;
@@ -3289,10 +3288,10 @@ static int loop_find_regions(BMEditMesh *em, const bool selbigger)
continue;
BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) {
- if (BLI_smallhash_haskey(&visithash, (uintptr_t)l->f))
+ if (BLI_gset_haskey(visit_face_set, l->f))
continue;
-
- c = loop_find_region(l, BM_ELEM_SELECT, &visithash, &region_out);
+
+ c = loop_find_region(l, BM_ELEM_SELECT, visit_face_set, &region_out);
if (!region || (selbigger ? c >= tot : c < tot)) {
/* this region is the best seen so far */
@@ -3327,7 +3326,7 @@ static int loop_find_regions(BMEditMesh *em, const bool selbigger)
}
MEM_freeN(edges);
- BLI_smallhash_release(&visithash);
+ BLI_gset_free(visit_face_set, NULL);
return count;
}
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index 8aea932e93a..655d96ae6e5 100644
--- a/source/blender/editors/mesh/mesh_intern.h
+++ b/source/blender/editors/mesh/mesh_intern.h
@@ -128,6 +128,7 @@ void MESH_OT_loopcut(struct wmOperatorType *ot);
/* *** editmesh_rip.c *** */
void MESH_OT_rip(struct wmOperatorType *ot);
+void MESH_OT_rip_edge(struct wmOperatorType *ot);
/* *** editmesh_select.c *** */
diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c
index 7c38ef21791..0f65ed2c4ad 100644
--- a/source/blender/editors/mesh/mesh_ops.c
+++ b/source/blender/editors/mesh/mesh_ops.c
@@ -142,6 +142,7 @@ void ED_operatortypes_mesh(void)
WM_operatortype_append(MESH_OT_noise);
WM_operatortype_append(MESH_OT_flip_normals);
WM_operatortype_append(MESH_OT_rip);
+ WM_operatortype_append(MESH_OT_rip_edge);
WM_operatortype_append(MESH_OT_blend_from_shape);
WM_operatortype_append(MESH_OT_shape_propagate_to_all);
@@ -239,6 +240,13 @@ void ED_operatormacros_mesh(void)
RNA_enum_set(otmacro->ptr, "proportional", 0);
RNA_boolean_set(otmacro->ptr, "mirror", false);
+ ot = WM_operatortype_append_macro("MESH_OT_rip_edge_move", "Extend Vertices", "Extend vertices and move the result",
+ OPTYPE_UNDO | OPTYPE_REGISTER);
+ WM_operatortype_macro_define(ot, "MESH_OT_rip_edge");
+ otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+ RNA_enum_set(otmacro->ptr, "proportional", 0);
+ RNA_boolean_set(otmacro->ptr, "mirror", false);
+
ot = WM_operatortype_append_macro("MESH_OT_extrude_region_move", "Extrude Region and Move",
"Extrude region and move result", OPTYPE_UNDO | OPTYPE_REGISTER);
otmacro = WM_operatortype_macro_define(ot, "MESH_OT_extrude_region");
@@ -372,6 +380,8 @@ void ED_keymap_mesh(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "MESH_OT_rip_move", VKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "MESH_OT_rip_move_fill", VKEY, KM_PRESS, KM_ALT, 0);
+ WM_keymap_add_item(keymap, "MESH_OT_rip_edge_move", DKEY, KM_PRESS, KM_ALT, 0);
+
WM_keymap_add_item(keymap, "MESH_OT_merge", MKEY, KM_PRESS, KM_ALT, 0);
WM_keymap_add_item(keymap, "TRANSFORM_OT_shrink_fatten", SKEY, KM_PRESS, KM_ALT, 0);
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index 9075ca26e40..b45ac124b43 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -302,11 +302,7 @@ static bool bake_object_check(Object *ob, ReportList *reports)
else {
Mesh *me = (Mesh *)ob->data;
- const int pidx = CustomData_get_active_layer_index(&me->pdata, CD_MTEXPOLY);
- const int lidx = CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV);
- const int fidx = CustomData_get_active_layer_index(&me->fdata, CD_MTFACE);
-
- if ((pidx == -1) && (lidx == -1) && (fidx == -1)) {
+ if (CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV) == -1) {
BKE_reportf(reports, RPT_ERROR,
"No active UV layer found in the object \"%s\"", ob->id.name + 2);
return false;
@@ -376,8 +372,8 @@ static bool bake_objects_check(Main *bmain, Object *ob, ListBase *selected_objec
if (ob_iter == ob)
continue;
- if (ob_iter->type != OB_MESH) {
- BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not a mesh", ob_iter->id.name + 2);
+ if (ELEM5(ob_iter->type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL) == false) {
+ BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not a mesh or can't be converted to a mesh (Curve, Text, Surface or Metaball)", ob_iter->id.name + 2);
return false;
}
tot_objects += 1;
@@ -493,11 +489,13 @@ typedef struct BakeAPIRender {
bool is_split_materials;
bool is_automatic_name;
bool is_selected_to_active;
+ bool is_cage;
float cage_extrusion;
int normal_space;
BakeNormalSwizzle normal_swizzle[3];
+ char uv_layer[MAX_CUSTOMDATA_LAYER_NAME];
char custom_cage[MAX_NAME];
char filepath[FILE_MAX];
@@ -516,10 +514,10 @@ static int bake(
Main *bmain, Scene *scene, Object *ob_low, ListBase *selected_objects, ReportList *reports,
const ScenePassType pass_type, const int margin,
const BakeSaveMode save_mode, const bool is_clear, const bool is_split_materials,
- const bool is_automatic_name, const bool is_selected_to_active,
+ const bool is_automatic_name, const bool is_selected_to_active, const bool is_cage,
const float cage_extrusion, const int normal_space, const BakeNormalSwizzle normal_swizzle[],
const char *custom_cage, const char *filepath, const int width, const int height,
- const char *identifier, ScrArea *sa)
+ const char *identifier, ScrArea *sa, const char *uv_layer)
{
int op_result = OPERATOR_CANCELLED;
bool ok = false;
@@ -533,6 +531,7 @@ static int bake(
char restrict_flag_cage;
Mesh *me_low = NULL;
+ Mesh *me_cage = NULL;
Render *re;
float *result = NULL;
@@ -552,8 +551,24 @@ static int bake(
re = RE_NewRender(scene->id.name);
RE_SetReports(re, NULL);
+ RE_bake_engine_set_engine_parameters(re, bmain, scene);
+
+ if (!RE_bake_has_engine(re)) {
+ BKE_report(reports, RPT_ERROR, "Current render engine does not support baking");
+ goto cleanup;
+ }
+
tot_materials = ob_low->totcol;
+ if (uv_layer && uv_layer[0] != '\0') {
+ Mesh *me = (Mesh *)ob_low->data;
+ if (CustomData_get_named_layer(&me->ldata, CD_MLOOPUV, uv_layer) == -1) {
+ BKE_reportf(reports, RPT_ERROR,
+ "No UV layer named \"%s\" found in the object \"%s\"", uv_layer, ob_low->id.name + 2);
+ goto cleanup;
+ }
+ }
+
if (tot_materials == 0) {
if (is_save_internal) {
BKE_report(reports, RPT_ERROR,
@@ -618,24 +633,20 @@ static int bake(
tot_highpoly ++;
}
- if (custom_cage[0] != '\0') {
+ if (is_cage && custom_cage[0] != '\0') {
ob_cage = BLI_findstring(&bmain->object, custom_cage, offsetof(ID, name) + 2);
- /* TODO check if cage object has the same topology (num of triangles and a valid UV) */
if (ob_cage == NULL || ob_cage->type != OB_MESH) {
BKE_report(reports, RPT_ERROR, "No valid cage object");
- op_result = OPERATOR_CANCELLED;
-
goto cleanup;
}
else {
restrict_flag_cage = ob_cage->restrictflag;
+ ob_cage->restrictflag |= OB_RESTRICT_RENDER;
}
}
}
- RE_bake_engine_set_engine_parameters(re, bmain, scene);
-
/* blender_test_break uses this global */
G.is_break = false;
@@ -644,20 +655,31 @@ static int bake(
pixel_array_low = MEM_callocN(sizeof(BakePixel) * num_pixels, "bake pixels low poly");
result = MEM_callocN(sizeof(float) * depth * num_pixels, "bake return pixels");
+ /* get the mesh as it arrives in the renderer */
+ me_low = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0);
+
+ /* populate the pixel array with the face data */
+ if ((is_selected_to_active && (ob_cage == NULL) && is_cage) == false)
+ RE_bake_pixels_populate(me_low, pixel_array_low, num_pixels, &bake_images, uv_layer);
+ /* else populate the pixel array with the 'cage' mesh (the smooth version of the mesh) */
+
if (is_selected_to_active) {
CollectionPointerLink *link;
ModifierData *md, *nmd;
ListBase modifiers_tmp, modifiers_original;
- float mat_low[4][4];
int i = 0;
- highpoly = MEM_callocN(sizeof(BakeHighPolyData) * tot_highpoly, "bake high poly objects");
/* prepare cage mesh */
if (ob_cage) {
- me_low = BKE_mesh_new_from_object(bmain, scene, ob_cage, 1, 2, 1, 0);
- copy_m4_m4(mat_low, ob_cage->obmat);
+ me_cage = BKE_mesh_new_from_object(bmain, scene, ob_cage, 1, 2, 1, 0);
+ if (me_low->totface != me_cage->totface) {
+ BKE_report(reports, RPT_ERROR,
+ "Invalid cage object, the cage mesh must have the same number "
+ "of faces as the active object");
+ goto cleanup;
+ }
}
- else {
+ else if (is_cage) {
modifiers_original = ob_low->modifiers;
BLI_listbase_clear(&modifiers_tmp);
@@ -681,10 +703,12 @@ static int bake(
ob_low->modifiers = modifiers_tmp;
/* get the cage mesh as it arrives in the renderer */
- me_low = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0);
- copy_m4_m4(mat_low, ob_low->obmat);
+ me_cage = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0);
+ RE_bake_pixels_populate(me_cage, pixel_array_low, num_pixels, &bake_images, uv_layer);
}
+ highpoly = MEM_callocN(sizeof(BakeHighPolyData) * tot_highpoly, "bake high poly objects");
+
/* populate highpoly array */
for (link = selected_objects->first; link; link = link->next) {
TriangulateModifierData *tmd;
@@ -727,50 +751,42 @@ static int bake(
BLI_assert(i == tot_highpoly);
- /* populate the pixel array with the face data */
- RE_bake_pixels_populate(me_low, pixel_array_low, num_pixels, &bake_images);
-
ob_low->restrictflag |= OB_RESTRICT_RENDER;
/* populate the pixel arrays with the corresponding face data for each high poly object */
- RE_bake_pixels_populate_from_objects(
- me_low, pixel_array_low, highpoly, tot_highpoly,
- num_pixels, cage_extrusion, mat_low);
+ if (!RE_bake_pixels_populate_from_objects(
+ me_low, pixel_array_low, highpoly, tot_highpoly, num_pixels, ob_cage != NULL,
+ cage_extrusion, ob_low->obmat, (ob_cage ? ob_cage->obmat : ob_low->obmat), me_cage))
+ {
+ BKE_report(reports, RPT_ERROR, "Error handling selected objects");
+ goto cage_cleanup;
+ }
/* the baking itself */
for (i = 0; i < tot_highpoly; i++) {
- if (RE_bake_has_engine(re)) {
- ok = RE_bake_engine(re, highpoly[i].ob, highpoly[i].pixel_array, num_pixels,
- depth, pass_type, result);
- }
- else {
- BKE_report(reports, RPT_ERROR, "Current render engine does not support baking");
- goto cleanup;
+ ok = RE_bake_engine(re, highpoly[i].ob, highpoly[i].pixel_array, num_pixels,
+ depth, pass_type, result);
+ if (!ok) {
+ BKE_reportf(reports, RPT_ERROR, "Error baking from object \"%s\"", highpoly[i].ob->id.name + 2);
+ goto cage_cleanup;
}
-
- if (!ok)
- break;
}
+cage_cleanup:
/* reverting data back */
- if (ob_cage) {
- ob_cage->restrictflag |= OB_RESTRICT_RENDER;
- }
- else {
+ if ((ob_cage == NULL) && is_cage) {
ob_low->modifiers = modifiers_original;
while ((md = BLI_pophead(&modifiers_tmp))) {
modifier_free(md);
}
}
+
+ if (!ok) {
+ goto cleanup;
+ }
}
else {
- /* get the mesh as it arrives in the renderer */
- me_low = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0);
-
- /* populate the pixel array with the face data */
- RE_bake_pixels_populate(me_low, pixel_array_low, num_pixels, &bake_images);
-
/* make sure low poly renders */
ob_low->restrictflag &= ~OB_RESTRICT_RENDER;
@@ -825,7 +841,7 @@ static int bake(
}
me_nores = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0);
- RE_bake_pixels_populate(me_nores, pixel_array_low, num_pixels, &bake_images);
+ RE_bake_pixels_populate(me_nores, pixel_array_low, num_pixels, &bake_images, uv_layer);
RE_bake_normal_world_to_tangent(pixel_array_low, num_pixels, depth, result, me_nores, normal_swizzle, ob_low->obmat);
BKE_libblock_free(bmain, me_nores);
@@ -969,6 +985,9 @@ cleanup:
if (me_low)
BKE_libblock_free(bmain, me_low);
+ if (me_cage)
+ BKE_libblock_free(bmain, me_cage);
+
return op_result;
}
@@ -992,6 +1011,7 @@ static void bake_init_api_data(wmOperator *op, bContext *C, BakeAPIRender *bkr)
bkr->is_split_materials = (!is_save_internal) && RNA_boolean_get(op->ptr, "use_split_materials");
bkr->is_automatic_name = RNA_boolean_get(op->ptr, "use_automatic_name");
bkr->is_selected_to_active = RNA_boolean_get(op->ptr, "use_selected_to_active");
+ bkr->is_cage = RNA_boolean_get(op->ptr, "use_cage");
bkr->cage_extrusion = RNA_float_get(op->ptr, "cage_extrusion");
bkr->normal_space = RNA_enum_get(op->ptr, "normal_space");
@@ -1003,7 +1023,9 @@ static void bake_init_api_data(wmOperator *op, bContext *C, BakeAPIRender *bkr)
bkr->height = RNA_int_get(op->ptr, "height");
bkr->identifier = "";
- RNA_string_get(op->ptr, "cage", bkr->custom_cage);
+ RNA_string_get(op->ptr, "uv_layer", bkr->uv_layer);
+
+ RNA_string_get(op->ptr, "cage_object", bkr->custom_cage);
if ((!is_save_internal) && bkr->is_automatic_name) {
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "type");
@@ -1040,9 +1062,10 @@ static int bake_exec(bContext *C, wmOperator *op)
result = bake(
bkr.main, bkr.scene, bkr.ob, &bkr.selected_objects, bkr.reports,
bkr.pass_type, bkr.margin, bkr.save_mode,
- bkr.is_clear, bkr.is_split_materials, bkr.is_automatic_name, true,
+ bkr.is_clear, bkr.is_split_materials, bkr.is_automatic_name, true, bkr.is_cage,
bkr.cage_extrusion, bkr.normal_space, bkr.normal_swizzle,
- bkr.custom_cage, bkr.filepath, bkr.width, bkr.height, bkr.identifier, bkr.sa);
+ bkr.custom_cage, bkr.filepath, bkr.width, bkr.height, bkr.identifier, bkr.sa,
+ bkr.uv_layer);
}
else {
CollectionPointerLink *link;
@@ -1052,9 +1075,10 @@ static int bake_exec(bContext *C, wmOperator *op)
result = bake(
bkr.main, bkr.scene, ob_iter, NULL, bkr.reports,
bkr.pass_type, bkr.margin, bkr.save_mode,
- is_clear, bkr.is_split_materials, bkr.is_automatic_name, false,
+ is_clear, bkr.is_split_materials, bkr.is_automatic_name, false, bkr.is_cage,
bkr.cage_extrusion, bkr.normal_space, bkr.normal_swizzle,
- bkr.custom_cage, bkr.filepath, bkr.width, bkr.height, bkr.identifier, bkr.sa);
+ bkr.custom_cage, bkr.filepath, bkr.width, bkr.height, bkr.identifier, bkr.sa,
+ bkr.uv_layer);
}
}
@@ -1080,9 +1104,10 @@ static void bake_startjob(void *bkv, short *UNUSED(stop), short *UNUSED(do_updat
bkr->result = bake(
bkr->main, bkr->scene, bkr->ob, &bkr->selected_objects, bkr->reports,
bkr->pass_type, bkr->margin, bkr->save_mode,
- bkr->is_clear, bkr->is_split_materials, bkr->is_automatic_name, true,
+ bkr->is_clear, bkr->is_split_materials, bkr->is_automatic_name, true, bkr->is_cage,
bkr->cage_extrusion, bkr->normal_space, bkr->normal_swizzle,
- bkr->custom_cage, bkr->filepath, bkr->width, bkr->height, bkr->identifier, bkr->sa);
+ bkr->custom_cage, bkr->filepath, bkr->width, bkr->height, bkr->identifier, bkr->sa,
+ bkr->uv_layer);
}
else {
CollectionPointerLink *link;
@@ -1092,9 +1117,10 @@ static void bake_startjob(void *bkv, short *UNUSED(stop), short *UNUSED(do_updat
bkr->result = bake(
bkr->main, bkr->scene, ob_iter, NULL, bkr->reports,
bkr->pass_type, bkr->margin, bkr->save_mode,
- is_clear, bkr->is_split_materials, bkr->is_automatic_name, false,
+ is_clear, bkr->is_split_materials, bkr->is_automatic_name, false, bkr->is_cage,
bkr->cage_extrusion, bkr->normal_space, bkr->normal_swizzle,
- bkr->custom_cage, bkr->filepath, bkr->width, bkr->height, bkr->identifier, bkr->sa);
+ bkr->custom_cage, bkr->filepath, bkr->width, bkr->height, bkr->identifier, bkr->sa,
+ bkr->uv_layer);
if (bkr->result == OPERATOR_CANCELLED)
return;
@@ -1147,7 +1173,7 @@ static void bake_set_props(wmOperator *op, Scene *scene)
RNA_property_float_set(op->ptr, prop, bake->cage_extrusion);
}
- prop = RNA_struct_find_property(op->ptr, "cage");
+ prop = RNA_struct_find_property(op->ptr, "cage_object");
if (!RNA_property_is_set(op->ptr, prop)) {
RNA_property_string_set(op->ptr, prop, bake->cage);
}
@@ -1182,6 +1208,11 @@ static void bake_set_props(wmOperator *op, Scene *scene)
RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_CLEAR));
}
+ prop = RNA_struct_find_property(op->ptr, "use_cage");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_CAGE));
+ }
+
prop = RNA_struct_find_property(op->ptr, "use_split_materials");
if (!RNA_property_is_set(op->ptr, prop)) {
RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_SPLIT_MAT));
@@ -1256,10 +1287,10 @@ void OBJECT_OT_bake(wmOperatorType *ot)
"Extends the baked result as a post process filter", 0, 64);
RNA_def_boolean(ot->srna, "use_selected_to_active", false, "Selected to Active",
"Bake shading on the surface of selected objects to the active object");
- RNA_def_float(ot->srna, "cage_extrusion", 0.0, 0.0, 1.0, "Cage Extrusion",
- "Distance to use for the inward ray cast when using selected to active", 0.0, 1.0);
- RNA_def_string(ot->srna, "cage", NULL, MAX_NAME, "Cage",
- "Object to use as cage");
+ RNA_def_float(ot->srna, "cage_extrusion", 0.0f, 0.0f, FLT_MAX, "Cage Extrusion",
+ "Distance to use for the inward ray cast when using selected to active", 0.0f, 1.0f);
+ RNA_def_string(ot->srna, "cage_object", NULL, MAX_NAME, "Cage Object",
+ "Object to use as cage, instead of calculating the cage from the active object with cage extrusion");
RNA_def_enum(ot->srna, "normal_space", normal_space_items, R_BAKE_SPACE_TANGENT, "Normal Space",
"Choose normal space for baking");
RNA_def_enum(ot->srna, "normal_r", normal_swizzle_items, R_BAKE_POSX, "R", "Axis to bake in red channel");
@@ -1269,8 +1300,11 @@ void OBJECT_OT_bake(wmOperatorType *ot)
"Choose how to save the baking map");
RNA_def_boolean(ot->srna, "use_clear", false, "Clear",
"Clear Images before baking (only for internal saving)");
+ RNA_def_boolean(ot->srna, "use_cage", false, "Cage",
+ "Cast rays to active object from a cage");
RNA_def_boolean(ot->srna, "use_split_materials", false, "Split Materials",
"Split baked maps per material, using material name in output file (external only)");
RNA_def_boolean(ot->srna, "use_automatic_name", false, "Automatic Name",
"Automatically name the output file with the pass type");
+ RNA_def_string(ot->srna, "uv_layer", NULL, MAX_CUSTOMDATA_LAYER_NAME, "UV Layer", "UV layer to override active");
}
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index 002bec4ef0b..aba792413af 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -1620,20 +1620,20 @@ static int object_mode_set_exec(bContext *C, wmOperator *op)
/* Exit current mode if it's not the mode we're setting */
if (mode != OB_MODE_OBJECT && (ob->mode != mode || toggle)) {
/* Enter new mode */
- WM_operator_name_call(C, object_mode_op_string(mode), WM_OP_EXEC_REGION_WIN, NULL);
+ ED_object_toggle_modes(C, mode);
}
if (toggle) {
/* Special case for Object mode! */
if (mode == OB_MODE_OBJECT && restore_mode == OB_MODE_OBJECT && ob->restore_mode != OB_MODE_OBJECT) {
- WM_operator_name_call(C, object_mode_op_string(ob->restore_mode), WM_OP_EXEC_REGION_WIN, NULL);
+ ED_object_toggle_modes(C, ob->restore_mode);
}
else if (ob->mode == mode) {
/* For toggling, store old mode so we know what to go back to */
ob->restore_mode = restore_mode;
}
else if (ob->restore_mode != OB_MODE_OBJECT && ob->restore_mode != mode) {
- WM_operator_name_call(C, object_mode_op_string(ob->restore_mode), WM_OP_EXEC_REGION_WIN, NULL);
+ ED_object_toggle_modes(C, ob->restore_mode);
}
}
@@ -1669,24 +1669,12 @@ void OBJECT_OT_mode_set(wmOperatorType *ot)
void ED_object_toggle_modes(bContext *C, int mode)
{
- /* Couldn't we use object_mode_op_string() here?
- * Also, if several bits are set in mode, several toggle ops will be called, is this expected?
- * If so, would be nice to explain why. ;) --mont29
- */
- if (mode & OB_MODE_SCULPT)
- WM_operator_name_call(C, "SCULPT_OT_sculptmode_toggle", WM_OP_EXEC_REGION_WIN, NULL);
- if (mode & OB_MODE_VERTEX_PAINT)
- WM_operator_name_call(C, "PAINT_OT_vertex_paint_toggle", WM_OP_EXEC_REGION_WIN, NULL);
- if (mode & OB_MODE_WEIGHT_PAINT)
- WM_operator_name_call(C, "PAINT_OT_weight_paint_toggle", WM_OP_EXEC_REGION_WIN, NULL);
- if (mode & OB_MODE_TEXTURE_PAINT)
- WM_operator_name_call(C, "PAINT_OT_texture_paint_toggle", WM_OP_EXEC_REGION_WIN, NULL);
- if (mode & OB_MODE_PARTICLE_EDIT)
- WM_operator_name_call(C, "PARTICLE_OT_particle_edit_toggle", WM_OP_EXEC_REGION_WIN, NULL);
- if (mode & OB_MODE_POSE)
- WM_operator_name_call(C, "OBJECT_OT_posemode_toggle", WM_OP_EXEC_REGION_WIN, NULL);
- if (mode & OB_MODE_EDIT)
- WM_operator_name_call(C, "OBJECT_OT_editmode_toggle", WM_OP_EXEC_REGION_WIN, NULL);
+ if (mode != OB_MODE_OBJECT) {
+ const char *opstring = object_mode_op_string(mode);
+ if (opstring) {
+ WM_operator_name_call(C, opstring, WM_OP_EXEC_REGION_WIN, NULL);
+ }
+ }
}
/************************ Game Properties ***********************/
diff --git a/source/blender/editors/object/object_lattice.c b/source/blender/editors/object/object_lattice.c
index 2af2ca3b0e9..3897e452d0d 100644
--- a/source/blender/editors/object/object_lattice.c
+++ b/source/blender/editors/object/object_lattice.c
@@ -285,7 +285,7 @@ static int lattice_select_mirror_exec(bContext *C, wmOperator *op)
const int i_flip = BKE_lattice_index_flip(lt, i, flip_uvw[0], flip_uvw[1], flip_uvw[2]);
bp = &lt->def[i];
if (!bp->hide) {
- if (BLI_BITMAP_GET(selpoints, i_flip)) {
+ if (BLI_BITMAP_TEST(selpoints, i_flip)) {
bp->f1 |= SELECT;
}
else {
@@ -338,7 +338,7 @@ static bool lattice_test_bitmap_uvw(Lattice *lt, BLI_bitmap *selpoints, int u, i
else {
int i = BKE_lattice_index_from_uvw(lt, u, v, w);
if (lt->def[i].hide == 0) {
- return (BLI_BITMAP_GET(selpoints, i) != 0) == selected;
+ return (BLI_BITMAP_TEST(selpoints, i) != 0) == selected;
}
return false;
}
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index 3e33268704c..1249beb4517 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -1650,9 +1650,9 @@ static void skin_armature_bone_create(Object *skin_ob,
int v;
/* ignore edge if already visited */
- if (BLI_BITMAP_GET(edges_visited, endx))
+ if (BLI_BITMAP_TEST(edges_visited, endx))
continue;
- BLI_BITMAP_SET(edges_visited, endx);
+ BLI_BITMAP_ENABLE(edges_visited, endx);
v = (e->v1 == parent_v ? e->v2 : e->v1);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 6ff21f75733..9d7a3598083 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -1679,7 +1679,7 @@ void OBJECT_OT_make_links_data(wmOperatorType *ot)
/* identifiers */
ot->name = "Link Data";
- ot->description = "Make links from the active object to other selected objects";
+ ot->description = "Apply active object links to other selected objects";
ot->idname = "OBJECT_OT_make_links_data";
/* api callbacks */
diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c
index b1a78407491..2e1e8a94871 100644
--- a/source/blender/editors/object/object_select.c
+++ b/source/blender/editors/object/object_select.c
@@ -315,22 +315,20 @@ static bool object_select_all_by_dup_group(bContext *C, Object *ob)
static bool object_select_all_by_particle(bContext *C, Object *ob)
{
+ ParticleSystem *psys_act = psys_get_current(ob);
bool changed = false;
CTX_DATA_BEGIN (C, Base *, base, visible_bases)
{
if ((base->flag & SELECT) == 0) {
- /* loop through other, then actives particles*/
+ /* loop through other particles*/
ParticleSystem *psys;
- ParticleSystem *psys_act;
-
+
for (psys = base->object->particlesystem.first; psys; psys = psys->next) {
- for (psys_act = ob->particlesystem.first; psys_act; psys_act = psys_act->next) {
- if (psys->part == psys_act->part) {
- base->flag |= SELECT;
- changed = true;
- break;
- }
+ if (psys->part == psys_act->part) {
+ base->flag |= SELECT;
+ changed = true;
+ break;
}
if (base->flag & SELECT) {
@@ -778,14 +776,29 @@ static bool select_grouped_gameprops(bContext *C, Object *ob)
return changed;
}
-static bool select_grouped_keyingset(bContext *C, Object *UNUSED(ob))
+static bool select_grouped_keyingset(bContext *C, Object *UNUSED(ob), ReportList *reports)
{
KeyingSet *ks = ANIM_scene_get_active_keyingset(CTX_data_scene(C));
bool changed = false;
/* firstly, validate KeyingSet */
- if ((ks == NULL) || (ANIM_validate_keyingset(C, NULL, ks) != 0))
+ if (ks == NULL) {
+ BKE_report(reports, RPT_ERROR, "No active Keying Set to use");
+ return false;
+ }
+ else if (ANIM_validate_keyingset(C, NULL, ks) != 0) {
+ if (ks->paths.first == NULL) {
+ if ((ks->flag & KEYINGSET_ABSOLUTE) == 0) {
+ BKE_report(reports, RPT_ERROR,
+ "Use another Keying Set, as the active one depends on the currently "
+ "selected objects or cannot find any targets due to unsuitable context");
+ }
+ else {
+ BKE_report(reports, RPT_ERROR, "Keying Set does not contain any paths");
+ }
+ }
return 0;
+ }
/* select each object that Keying Set refers to */
/* TODO: perhaps to be more in line with the rest of these, we should only take objects
@@ -854,7 +867,7 @@ static int object_select_grouped_exec(bContext *C, wmOperator *op)
else if (nr == 9) changed |= select_grouped_index_object(C, ob);
else if (nr == 10) changed |= select_grouped_color(C, ob);
else if (nr == 11) changed |= select_grouped_gameprops(C, ob);
- else if (nr == 12) changed |= select_grouped_keyingset(C, ob);
+ else if (nr == 12) changed |= select_grouped_keyingset(C, ob, op->reports);
else if (nr == 13) changed |= select_similar_lamps(C, ob);
else if (nr == 14) changed |= select_similar_pass_index(C, ob);
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index 3c6bdf86a50..5432f059405 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -616,7 +616,10 @@ static void foreach_mouse_hit_key(PEData *data, ForKeyMatFunc func, int selected
ParticleSystemModifierData *psmd = NULL;
ParticleEditSettings *pset= PE_settings(data->scene);
POINT_P; KEY_K;
- float mat[4][4] = MAT4_UNITY, imat[4][4] = MAT4_UNITY;
+ float mat[4][4], imat[4][4];
+
+ unit_m4(mat);
+ unit_m4(imat);
if (edit->psys)
psmd= psys_get_modifier(data->ob, edit->psys);
@@ -1710,11 +1713,13 @@ int PE_lasso_select(bContext *C, const int mcords[][2], const short moves, bool
ParticleSystem *psys = edit->psys;
ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
POINT_P; KEY_K;
- float co[3], mat[4][4] = MAT4_UNITY;
+ float co[3], mat[4][4];
int screen_co[2];
PEData data;
+ unit_m4(mat);
+
if (!PE_start_edit(edit))
return OPERATOR_CANCELLED;
diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c
index 25bc2aeed07..5a61f77e5a8 100644
--- a/source/blender/editors/physics/particle_object.c
+++ b/source/blender/editors/physics/particle_object.c
@@ -633,7 +633,8 @@ static bool connect_hair(Scene *scene, Object *ob, ParticleSystem *psys)
HairKey *key;
BVHTreeFromMesh bvhtree= {NULL};
BVHTreeNearest nearest;
- MFace *mface, *mf;
+ MFace *mface = NULL, *mf;
+ MEdge *medge = NULL, *me;
MVert *mvert;
DerivedMesh *dm = NULL;
int numverts;
@@ -663,13 +664,23 @@ static bool connect_hair(Scene *scene, Object *ob, ParticleSystem *psys)
numverts = dm->getNumVerts(dm);
mvert = dm->getVertArray(dm);
- mface = dm->getTessFaceArray(dm);
/* convert to global coordinates */
for (i=0; i<numverts; i++)
mul_m4_v3(ob->obmat, mvert[i].co);
- bvhtree_from_mesh_faces(&bvhtree, dm, 0.0, 2, 6);
+ if (dm->getNumTessFaces(dm) != 0) {
+ mface = dm->getTessFaceArray(dm);
+ bvhtree_from_mesh_faces(&bvhtree, dm, 0.0, 2, 6);
+ }
+ else if (dm->getNumEdges(dm) != 0) {
+ medge = dm->getEdgeArray(dm);
+ bvhtree_from_mesh_edges(&bvhtree, dm, 0.0, 2, 6);
+ }
+ else {
+ dm->release(dm);
+ return false;
+ }
for (i=0, pa= psys->particles; i<psys->totpart; i++, pa++) {
key = pa->hair;
@@ -685,21 +696,35 @@ static bool connect_hair(Scene *scene, Object *ob, ParticleSystem *psys)
continue;
}
- mf = &mface[nearest.index];
+ if (mface) {
+ mf = &mface[nearest.index];
+
+ copy_v3_v3(v[0], mvert[mf->v1].co);
+ copy_v3_v3(v[1], mvert[mf->v2].co);
+ copy_v3_v3(v[2], mvert[mf->v3].co);
+ if (mf->v4) {
+ copy_v3_v3(v[3], mvert[mf->v4].co);
+ interp_weights_poly_v3(pa->fuv, v, 4, nearest.co);
+ }
+ else
+ interp_weights_poly_v3(pa->fuv, v, 3, nearest.co);
+
+ pa->num = nearest.index;
+ pa->num_dmcache = psys_particle_dm_face_lookup(ob, psmd->dm, pa->num, pa->fuv, NULL);
+ }
+ else {
+ me = &medge[nearest.index];
+
+ pa->fuv[1] = line_point_factor_v3(nearest.co,
+ mvert[me->v2].co,
+ mvert[me->v2].co);
+ pa->fuv[0] = 1.0f - pa->fuv[1];
+ pa->fuv[2] = pa->fuv[3] = 0.0f;
- copy_v3_v3(v[0], mvert[mf->v1].co);
- copy_v3_v3(v[1], mvert[mf->v2].co);
- copy_v3_v3(v[2], mvert[mf->v3].co);
- if (mf->v4) {
- copy_v3_v3(v[3], mvert[mf->v4].co);
- interp_weights_poly_v3(pa->fuv, v, 4, nearest.co);
+ pa->num = nearest.index;
+ pa->num_dmcache = -1;
}
- else
- interp_weights_poly_v3(pa->fuv, v, 3, nearest.co);
- pa->num = nearest.index;
- pa->num_dmcache = psys_particle_dm_face_lookup(ob, psmd->dm, pa->num, pa->fuv, NULL);
-
psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat);
invert_m4_m4(imat, hairmat);
diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c
index 9fa312884da..19290ef0f48 100644
--- a/source/blender/editors/render/render_internal.c
+++ b/source/blender/editors/render/render_internal.c
@@ -35,6 +35,7 @@
#include "BLI_blenlib.h"
#include "BLI_math.h"
+#include "BLI_threads.h"
#include "BLI_utildefines.h"
#include "PIL_time.h"
@@ -316,10 +317,12 @@ static int screen_render_exec(bContext *C, wmOperator *op)
RE_SetReports(re, op->reports);
+ BLI_begin_threaded_malloc();
if (is_animation)
RE_BlenderAnim(re, mainp, scene, camera_override, lay_override, scene->r.sfra, scene->r.efra, scene->r.frame_step);
else
RE_BlenderFrame(re, mainp, scene, srl, camera_override, lay_override, scene->r.cfra, is_write_still);
+ BLI_end_threaded_malloc();
RE_SetReports(re, NULL);
@@ -1011,6 +1014,8 @@ void RENDER_OT_render(wmOperatorType *ot)
#define PR_UPDATE_MATERIAL 4
#define PR_UPDATE_DATABASE 8
+#define START_RESOLUTION_DIVIDER 8
+
typedef struct RenderPreview {
/* from wmJob */
void *owner;
@@ -1026,6 +1031,8 @@ typedef struct RenderPreview {
RenderEngine *engine;
float viewmat[4][4];
+
+ int resolution_divider;
} RenderPreview;
static int render_view3d_disprect(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D *rv3d, rcti *disprect)
@@ -1127,7 +1134,6 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda
RenderPreview *rp = customdata;
Render *re;
RenderStats *rstats;
- RenderData rdata;
rctf viewplane;
rcti cliprct;
float clipsta, clipend, pixsize;
@@ -1161,23 +1167,36 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda
rstats = RE_GetStats(re);
+ if (update_flag & PR_UPDATE_VIEW) {
+ rp->resolution_divider = START_RESOLUTION_DIVIDER;
+ }
+
if ((update_flag & (PR_UPDATE_RENDERSIZE | PR_UPDATE_DATABASE)) || rstats->convertdone == 0) {
+ RenderData rdata;
+ int winx = rp->ar->winx / rp->resolution_divider,
+ winy = rp->ar->winy / rp->resolution_divider;
+
/* no osa, blur, seq, layers, etc for preview render */
rdata = rp->scene->r;
rdata.mode &= ~(R_OSA | R_MBLUR | R_BORDER | R_PANORAMA);
rdata.scemode &= ~(R_DOSEQ | R_DOCOMP | R_FREE_IMAGE);
rdata.scemode |= R_VIEWPORT_PREVIEW;
-
+
/* we do use layers, but only active */
rdata.scemode |= R_SINGLE_LAYER;
/* initalize always */
if (render_view3d_disprect(rp->scene, rp->ar, rp->v3d, rp->rv3d, &cliprct)) {
rdata.mode |= R_BORDER;
- RE_InitState(re, NULL, &rdata, NULL, rp->ar->winx, rp->ar->winy, &cliprct);
+ RE_InitState(re, NULL, &rdata, NULL, winx, winy, &cliprct);
}
else
- RE_InitState(re, NULL, &rdata, NULL, rp->ar->winx, rp->ar->winy, NULL);
+ RE_InitState(re, NULL, &rdata, NULL, winx, winy, NULL);
+ }
+ else if (update_flag & PR_UPDATE_VIEW) {
+ int winx = rp->ar->winx / rp->resolution_divider,
+ winy = rp->ar->winy / rp->resolution_divider;
+ RE_ChangeResolution(re, winx, winy, NULL);
}
if (orth)
@@ -1225,8 +1244,30 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda
/* OK, can we enter render code? */
if (rstats->convertdone) {
- RE_TileProcessor(re);
-
+ for (;;) {
+ RE_TileProcessor(re);
+
+ if (!*stop && rp->resolution_divider > 1) {
+ int winx, winy;
+ rp->resolution_divider /= 2;
+ winx = rp->ar->winx / rp->resolution_divider;
+ winy = rp->ar->winy / rp->resolution_divider;
+ *do_update = 1;
+ RE_ChangeResolution(re, winx, winy, NULL);
+
+ /* Otherwise shadows are incorrect. */
+ if (orth) {
+ RE_SetOrtho(re, &viewplane, clipsta, clipend);
+ }
+ else {
+ RE_SetWindow(re, &viewplane, clipsta, clipend);
+ }
+ }
+ else {
+ break;
+ }
+ }
+
/* always rotate back */
if (restore)
RE_DataBase_IncrementalView(re, rp->viewmat, 1);
@@ -1341,6 +1382,7 @@ static void render_view3d_do(RenderEngine *engine, const bContext *C)
rp->v3d = rp->sa->spacedata.first;
rp->rv3d = CTX_wm_region_view3d(C);
rp->bmain = CTX_data_main(C);
+ rp->resolution_divider = START_RESOLUTION_DIVIDER;
copy_m4_m4(rp->viewmat, rp->rv3d->viewmat);
/* clear info text */
@@ -1386,23 +1428,25 @@ void render_view3d_draw(RenderEngine *engine, const bContext *C)
if (rres.rectf) {
Scene *scene = CTX_data_scene(C);
+ ARegion *ar = CTX_wm_region(C);
bool force_fallback = false;
bool need_fallback = true;
float dither = scene->r.dither_intensity;
-
- /* Dithering is not supported on GLSL yet */
- force_fallback |= dither != 0.0f;
+ float scale_x = (float) ar->winx / rres.rectx;
+ float scale_y = (float) ar->winy / rres.recty;
/* If user decided not to use GLSL, fallback to glaDrawPixelsAuto */
force_fallback |= (U.image_draw_method != IMAGE_DRAW_METHOD_GLSL);
/* Try using GLSL display transform. */
if (force_fallback == false) {
- if (IMB_colormanagement_setup_glsl_draw(&scene->view_settings, &scene->display_settings, 0.0f, true)) {
+ if (IMB_colormanagement_setup_glsl_draw(&scene->view_settings, &scene->display_settings, dither, true)) {
glEnable(GL_BLEND);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glPixelZoom(scale_x, scale_y);
glaDrawPixelsTex(rres.xof, rres.yof, rres.rectx, rres.recty, GL_RGBA, GL_FLOAT,
GL_NEAREST, rres.rectf);
+ glPixelZoom(1.0f, 1.0f);
glDisable(GL_BLEND);
IMB_colormanagement_finish_glsl_draw();
@@ -1420,8 +1464,10 @@ void render_view3d_draw(RenderEngine *engine, const bContext *C)
glEnable(GL_BLEND);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glPixelZoom(scale_x, scale_y);
glaDrawPixelsAuto(rres.xof, rres.yof, rres.rectx, rres.recty, GL_RGBA, GL_UNSIGNED_BYTE,
GL_NEAREST, display_buffer);
+ glPixelZoom(1.0f, 1.0f);
glDisable(GL_BLEND);
MEM_freeN(display_buffer);
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index 3ea5be6405a..559c86bf48c 100644
--- a/source/blender/editors/render/render_opengl.c
+++ b/source/blender/editors/render/render_opengl.c
@@ -34,6 +34,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
+#include "BLI_math_color_blend.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "BLI_jitter.h"
@@ -55,6 +56,7 @@
#include "ED_screen.h"
#include "ED_view3d.h"
+#include "ED_gpencil.h"
#include "RE_pipeline.h"
#include "IMB_imbuf_types.h"
@@ -138,7 +140,9 @@ static void screen_opengl_render_apply(OGLRender *oglrender)
if (oglrender->is_sequencer) {
SeqRenderData context;
- int chanshown = oglrender->sseq ? oglrender->sseq->chanshown : 0;
+ SpaceSeq *sseq = oglrender->sseq;
+ int chanshown = sseq ? sseq->chanshown : 0;
+ struct bGPdata *gpd = (sseq && (sseq->flag & SEQ_SHOW_GPENCIL)) ? sseq->gpd : NULL;
context = BKE_sequencer_new_render_data(oglrender->bmain->eval_ctx, oglrender->bmain,
scene, oglrender->sizex, oglrender->sizey, 100.0f);
@@ -170,6 +174,33 @@ static void screen_opengl_render_apply(OGLRender *oglrender)
IMB_freeImBuf(linear_ibuf);
}
+
+ if (gpd) {
+ int i;
+ unsigned char *gp_rect;
+
+ GPU_offscreen_bind(oglrender->ofs);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ wmOrtho2(0, sizex, 0, sizey);
+ glTranslatef(sizex / 2, sizey / 2, 0.0f);
+
+ ED_gpencil_draw_ex(gpd, sizex, sizey, scene->r.cfra);
+
+ gp_rect = MEM_mallocN(sizex * sizey * sizeof(unsigned char) * 4, "offscreen rect");
+ GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, gp_rect);
+
+ for (i = 0; i < sizex * sizey * 4; i += 4) {
+ float col_src[4];
+ rgba_uchar_to_float(col_src, &gp_rect[i]);
+ blend_color_mix_float(&rr->rectf[i], &rr->rectf[i], col_src);
+ }
+ GPU_offscreen_unbind(oglrender->ofs);
+
+ MEM_freeN(gp_rect);
+ }
}
else if (view_context) {
ED_view3d_draw_offscreen_init(scene, v3d);
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index 46777685442..01f00a8458a 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -299,7 +299,7 @@ static Scene *preview_prepare_scene(Scene *scene, ID *id, int id_type, ShaderPre
sce->r.cfra = scene->r.cfra;
- if (id_type == ID_TE && ELEM(sp->pr_method, PR_ICON_RENDER, PR_NODE_RENDER)) {
+ if (id_type == ID_TE) {
/* Force blender internal for texture icons and nodes render,
* seems commonly used render engines does not support
* such kind of rendering.
@@ -1137,6 +1137,13 @@ void ED_preview_shader_job(const bContext *C, void *owner, ID *id, ID *parent, M
wmJob *wm_job;
ShaderPreview *sp;
Scene *scene = CTX_data_scene(C);
+ short id_type = GS(id->name);
+ bool use_new_shading = BKE_scene_use_new_shading_nodes(scene);
+
+ /* Only texture node preview is supported with Cycles. */
+ if (use_new_shading && method == PR_NODE_RENDER && id_type != ID_TE) {
+ return;
+ }
wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), owner, "Shader Preview",
WM_JOB_EXCL_RENDER, WM_JOB_TYPE_RENDER_PREVIEW);
@@ -1154,10 +1161,12 @@ void ED_preview_shader_job(const bContext *C, void *owner, ID *id, ID *parent, M
/* hardcoded preview .blend for cycles/internal, this should be solved
* once with custom preview .blend path for external engines */
- if (BKE_scene_use_new_shading_nodes(scene) && method != PR_NODE_RENDER)
+ if ((method != PR_NODE_RENDER) && id_type != ID_TE && use_new_shading) {
sp->pr_main = G_pr_main_cycles;
- else
+ }
+ else {
sp->pr_main = G_pr_main;
+ }
if (ob && ob->totcol) copy_v4_v4(sp->col, ob->col);
else sp->col[0] = sp->col[1] = sp->col[2] = sp->col[3] = 1.0f;
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index 276fc9aca28..1727f38c4b3 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -472,6 +472,7 @@ void ED_region_tag_redraw(ARegion *ar)
* but python scripts can cause this to happen indirectly */
if (ar && !(ar->do_draw & RGN_DRAWING)) {
/* zero region means full region redraw */
+ ar->do_draw &= ~RGN_DRAW_PARTIAL; /* just incase */
ar->do_draw = RGN_DRAW;
memset(&ar->drawrct, 0, sizeof(ar->drawrct));
}
@@ -483,6 +484,13 @@ void ED_region_tag_redraw_overlay(ARegion *ar)
ar->do_draw_overlay = RGN_DRAW;
}
+void ED_region_tag_refresh_ui(ARegion *ar)
+{
+ if (ar) {
+ ar->do_draw |= RGN_DRAW_REFRESH_UI;
+ }
+}
+
void ED_region_tag_redraw_partial(ARegion *ar, rcti *rct)
{
if (ar && !(ar->do_draw & RGN_DRAWING)) {
@@ -1339,10 +1347,6 @@ void ED_region_init(bContext *C, ARegion *ar)
region_subwindow(CTX_wm_window(C), ar);
region_update_rect(ar);
-
- /* UI convention */
- wmOrtho2(-0.01f, ar->winx - 0.01f, -0.01f, ar->winy - 0.01f);
- glLoadIdentity();
}
/* for quick toggle, can skip fades */
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 8b6a1b22a5c..2ddda19fb28 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -166,7 +166,7 @@ int ED_operator_objectmode(bContext *C)
return 0;
/* add a check for ob->mode too? */
- if (obact && obact->mode)
+ if (obact && (obact->mode != OB_MODE_OBJECT))
return 0;
return 1;
@@ -267,7 +267,6 @@ int ED_operator_node_editable(bContext *C)
return 0;
}
-/* XXX rename */
int ED_operator_graphedit_active(bContext *C)
{
return ed_spacetype_test(C, SPACE_IPO);
diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c
index bc37a88d7f0..d1a7f0032ae 100644
--- a/source/blender/editors/sculpt_paint/paint_hide.c
+++ b/source/blender/editors/sculpt_paint/paint_hide.c
@@ -192,14 +192,14 @@ static void partialvis_update_grids(Object *ob,
/* skip grid element if not in the effected area */
if (is_effected(area, planes, co, mask)) {
/* set or clear the hide flag */
- BLI_BITMAP_MODIFY(gh, y * key.grid_size + x,
+ BLI_BITMAP_SET(gh, y * key.grid_size + x,
action == PARTIALVIS_HIDE);
any_changed = true;
}
/* keep track of whether any elements are still hidden */
- if (BLI_BITMAP_GET(gh, y * key.grid_size + x))
+ if (BLI_BITMAP_TEST(gh, y * key.grid_size + x))
any_hidden = true;
else
any_visible = true;
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index d0def6b07d6..34232c51ff7 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -69,8 +69,8 @@
#include "ED_image.h"
#include "ED_object.h"
+#include "ED_paint.h"
#include "ED_screen.h"
-#include "ED_sculpt.h"
#include "ED_view3d.h"
#include "WM_api.h"
diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c
index 667b487d4b1..4f67fc9cc87 100644
--- a/source/blender/editors/sculpt_paint/paint_image_2d.c
+++ b/source/blender/editors/sculpt_paint/paint_image_2d.c
@@ -46,8 +46,8 @@
#include "BKE_paint.h"
#include "BKE_report.h"
+#include "ED_paint.h"
#include "ED_screen.h"
-#include "ED_sculpt.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index cb2a94a0530..399b5044a05 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -73,8 +73,8 @@
#include "UI_view2d.h"
+#include "ED_paint.h"
#include "ED_screen.h"
-#include "ED_sculpt.h"
#include "ED_uvedit.h"
#include "ED_view3d.h"
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index 9363542ba05..364ebac42a7 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -184,7 +184,7 @@ static void flip_plane(float out[4], const float in[4], const char symm)
out[3] = in[3];
}
-int do_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, rcti *rect, bool select, bool UNUSED(extend))
+int ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti *rect, bool select, bool UNUSED(extend))
{
Sculpt *sd = vc->scene->toolsettings->sculpt;
BoundBox bb;
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index 543463cd5f3..9021581d47f 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -40,7 +40,7 @@
#include "BKE_paint.h"
#include "BKE_main.h"
-#include "ED_sculpt.h"
+#include "ED_paint.h"
#include "ED_screen.h"
#include "ED_image.h"
#include "UI_resources.h"
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 7b9121446f5..93408586704 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -336,8 +336,13 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, const float
}
/* TODO: can remove the if statement once all modes have this */
- if (stroke->get_location)
- stroke->get_location(C, location, mouse_out);
+ if (stroke->get_location) {
+ if (!stroke->get_location(C, location, mouse_out)) {
+ if (ar && (paint->flags & PAINT_SHOW_BRUSH))
+ WM_paint_cursor_tag_redraw(window, ar);
+ return;
+ }
+ }
else
zero_v3(location);
@@ -794,7 +799,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (event->type != INBETWEEN_MOUSEMOVE)
if (redraw && stroke->redraw)
stroke->redraw(C, stroke, false);
-
+
return OPERATOR_RUNNING_MODAL;
}
diff --git a/source/blender/editors/sculpt_paint/paint_undo.c b/source/blender/editors/sculpt_paint/paint_undo.c
index 5bd6526c270..c5c747dbab4 100644
--- a/source/blender/editors/sculpt_paint/paint_undo.c
+++ b/source/blender/editors/sculpt_paint/paint_undo.c
@@ -38,7 +38,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
-#include "ED_sculpt.h"
+#include "ED_paint.h"
#include "paint_intern.h"
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index abbb8dd55ce..12223effcf5 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -88,6 +88,9 @@
#include "GPU_buffers.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
#include "bmesh.h"
#include "bmesh_tools.h"
@@ -112,7 +115,7 @@ static int system_physical_thread_count(void)
}
#endif /* __APPLE__ */
-void ED_sculpt_get_average_stroke(Object *ob, float stroke[3])
+void ED_sculpt_stroke_get_average(Object *ob, float stroke[3])
{
if (ob->sculpt->last_stroke_valid && ob->sculpt->average_stroke_counter > 0) {
float fac = 1.0f / ob->sculpt->average_stroke_counter;
@@ -178,7 +181,7 @@ typedef struct StrokeCache {
float initial_mouse[2];
/* Pre-allocated temporary storage used during smoothing */
- int num_threads, max_threads;
+ int num_threads, init_num_threads;
float (**tmpgrid_co)[3], (**tmprow_co)[3];
float **tmpgrid_mask, **tmprow_mask;
@@ -223,7 +226,7 @@ typedef struct StrokeCache {
float sculpt_normal[3];
float sculpt_normal_symm[3];
- /* Used for wrap texture mode, local_mat gets calculated by
+ /* Used for area texture mode, local_mat gets calculated by
* calc_brush_local_mat() and used in tex_strength(). */
float brush_local_mat[4][4];
@@ -486,8 +489,8 @@ static bool sculpt_get_redraw_rect(ARegion *ar, RegionView3D *rv3d,
return 1;
}
-void sculpt_get_redraw_planes(float planes[4][4], ARegion *ar,
- RegionView3D *rv3d, Object *ob)
+void ED_sculpt_redraw_planes_get(float planes[4][4], ARegion *ar,
+ RegionView3D *rv3d, Object *ob)
{
PBVH *pbvh = ob->sculpt->pbvh;
/* copy here, original will be used below */
@@ -870,7 +873,6 @@ static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather)
static float tex_strength(SculptSession *ss, Brush *br,
const float point[3],
const float len,
- const float sculpt_normal[3],
const short vno[3],
const float fno[3],
const float mask)
@@ -941,7 +943,7 @@ static float tex_strength(SculptSession *ss, Brush *br,
/* Falloff curve */
avg *= BKE_brush_curve_strength(br, len, cache->radius);
- avg *= frontface(br, sculpt_normal, vno, fno);
+ avg *= frontface(br, cache->view_normal, vno, fno);
/* Paint mask */
avg *= 1.0f - mask;
@@ -1232,8 +1234,7 @@ static int brush_needs_sculpt_normal(const Brush *brush)
return ((ELEM(brush->sculpt_tool,
SCULPT_TOOL_GRAB,
SCULPT_TOOL_SNAKE_HOOK) &&
- ((brush->normal_weight > 0) ||
- (brush->flag & BRUSH_FRONTFACE))) ||
+ (brush->normal_weight > 0)) ||
ELEM7(brush->sculpt_tool,
SCULPT_TOOL_BLOB,
@@ -1397,7 +1398,7 @@ static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node,
{
if (sculpt_brush_test(&test, vd.co)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist,
- ss->cache->view_normal, vd.no, vd.fno,
+ vd.no, vd.fno,
smooth_mask ? 0 : (vd.mask ? *vd.mask : 0.0f));
if (smooth_mask) {
float val = neighbor_average_mask(ss, vd.vert_indices[vd.i]) - *vd.mask;
@@ -1438,7 +1439,7 @@ static void do_bmesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node,
{
if (sculpt_brush_test(&test, vd.co)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist,
- ss->cache->view_normal, vd.no, vd.fno,
+ vd.no, vd.fno,
smooth_mask ? 0 : *vd.mask);
if (smooth_mask) {
float val = bmesh_neighbor_average_mask(ss->bm, vd.bm_vert) - *vd.mask;
@@ -1566,7 +1567,7 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no
int index;
if (gh) {
- if (BLI_BITMAP_GET(gh, y * gridsize + x))
+ if (BLI_BITMAP_TEST(gh, y * gridsize + x))
continue;
}
@@ -1590,7 +1591,6 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no
if (sculpt_brush_test(&test, co)) {
const float strength_mask = (smooth_mask ? 0 : *mask);
const float fade = bstrength * tex_strength(ss, brush, co, test.dist,
- ss->cache->view_normal,
NULL, fno, strength_mask);
float n = 1.0f / 16.0f;
@@ -1693,7 +1693,7 @@ static void do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int tot
{
if (sculpt_brush_test(&test, vd.co)) {
float fade = tex_strength(ss, brush, vd.co, test.dist,
- ss->cache->view_normal, vd.no, vd.fno, 0);
+ vd.no, vd.fno, 0);
(*vd.mask) += fade * bstrength;
CLAMP(*vd.mask, 0, 1);
@@ -1749,8 +1749,7 @@ static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
if (sculpt_brush_test(&test, vd.co)) {
/* offset vertex */
- float fade = tex_strength(ss, brush, vd.co, test.dist,
- ss->cache->sculpt_normal_symm, vd.no,
+ float fade = tex_strength(ss, brush, vd.co, test.dist, vd.no,
vd.fno, vd.mask ? *vd.mask : 0.0f);
mul_v3_v3fl(proxy[vd.i], offset, fade);
@@ -1806,7 +1805,6 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
if (sculpt_brush_test(&test, vd.co)) {
/* offset vertex */
const float fade = tex_strength(ss, brush, vd.co, test.dist,
- ss->cache->sculpt_normal_symm,
vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f);
float val1[3];
float val2[3];
@@ -1848,8 +1846,7 @@ static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test(&test, vd.co)) {
- float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist,
- ss->cache->view_normal, vd.no,
+ float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist, vd.no,
vd.fno, vd.mask ? *vd.mask : 0.0f);
float val[3];
@@ -1904,7 +1901,6 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
const float fade = bstrength * tex_strength(ss, brush,
orig_data.co,
test.dist,
- ss->cache->sculpt_normal_symm,
orig_data.no,
NULL, vd.mask ? *vd.mask : 0.0f);
@@ -1946,7 +1942,6 @@ static void do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
{
if (sculpt_brush_test(&test, vd.co)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist,
- ss->cache->sculpt_normal_symm,
vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f);
mul_v3_v3fl(proxy[vd.i], cono, fade);
@@ -1995,7 +1990,6 @@ static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to
{
if (sculpt_brush_test(&test, vd.co)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist,
- ss->cache->sculpt_normal_symm,
vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f);
mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
@@ -2043,7 +2037,6 @@ static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
const float fade = bstrength * tex_strength(ss, brush,
orig_data.co,
test.dist,
- ss->cache->sculpt_normal_symm,
orig_data.no,
NULL, vd.mask ? *vd.mask : 0.0f);
@@ -2088,7 +2081,6 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
const float fade = bstrength * tex_strength(ss, brush,
orig_data.co,
test.dist,
- ss->cache->sculpt_normal_symm,
orig_data.no,
NULL, vd.mask ? *vd.mask : 0.0f);
@@ -2144,7 +2136,6 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
if (sculpt_brush_test(&test, orig_data.co)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist,
- ss->cache->sculpt_normal_symm,
vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f);
float *disp = &layer_disp[vd.i];
float val[3];
@@ -2198,7 +2189,6 @@ static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno
{
if (sculpt_brush_test(&test, vd.co)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist,
- ss->cache->view_normal,
vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f);
float val[3];
@@ -2611,7 +2601,7 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno
if (plane_trim(ss->cache, brush, val)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co, sqrt(test.dist),
- an, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f);
+ vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -2683,9 +2673,8 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
sub_v3_v3v3(val, intr, vd.co);
if (plane_trim(ss->cache, brush, val)) {
- const float fade = bstrength * tex_strength(ss, brush, vd.co,
- sqrt(test.dist),
- an, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f);
+ const float fade = bstrength * tex_strength(ss, brush, vd.co, sqrt(test.dist),
+ vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -2787,7 +2776,7 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t
if (plane_trim(ss->cache, brush, val)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co,
ss->cache->radius * test.dist,
- an, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f);
+ vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -2851,7 +2840,7 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
if (plane_trim(ss->cache, brush, val)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co,
sqrt(test.dist),
- an, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f);
+ vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -2915,7 +2904,7 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
if (plane_trim(ss->cache, brush, val)) {
const float fade = bstrength * tex_strength(ss, brush, vd.co,
sqrt(test.dist),
- an, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f);
+ vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -2957,8 +2946,7 @@ static void do_gravity(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, fl
BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
if (sculpt_brush_test_sq(&test, vd.co)) {
- const float fade = tex_strength(ss, brush, vd.co, sqrt(test.dist),
- ss->cache->sculpt_normal_symm, vd.no,
+ const float fade = tex_strength(ss, brush, vd.co, sqrt(test.dist), vd.no,
vd.fno, vd.mask ? *vd.mask : 0.0f);
mul_v3_v3fl(proxy[vd.i], offset, fade);
@@ -3624,6 +3612,12 @@ static void sculpt_omp_start(Sculpt *sd, SculptSession *ss)
StrokeCache *cache = ss->cache;
#ifdef _OPENMP
+
+#if defined(__APPLE__)
+ cache->init_num_threads = BLI_system_thread_count();
+#else
+ cache->init_num_threads = omp_get_max_threads();
+#endif
/* If using OpenMP then create a number of threads two times the
* number of processor cores.
* Justification: Empirically I've found that two threads per
@@ -3632,13 +3626,12 @@ static void sculpt_omp_start(Sculpt *sd, SculptSession *ss)
#if defined(__APPLE__)
cache->num_threads = system_physical_thread_count();
#else
- cache->num_threads = omp_get_num_procs();
+ cache->num_threads = 2 * omp_get_num_procs();
#endif
}
else {
cache->num_threads = 1;
}
- cache->max_threads = omp_get_max_threads();
omp_set_num_threads(cache->num_threads);
#else
(void)sd;
@@ -3671,8 +3664,9 @@ static void sculpt_omp_start(Sculpt *sd, SculptSession *ss)
static void sculpt_omp_done(SculptSession *ss)
{
#ifdef _OPENMP
- omp_set_num_threads(ss->cache->max_threads);
+ omp_set_num_threads(ss->cache->init_num_threads);
#endif
+
if (ss->multires) {
int i;
@@ -4474,7 +4468,7 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
/* update last stroke position */
ob->sculpt->last_stroke_valid = 1;
- ED_sculpt_get_average_stroke(ob, ob->sculpt->last_stroke);
+ ED_sculpt_stroke_get_average(ob, ob->sculpt->last_stroke);
sculpt_cache_free(ss->cache);
ss->cache = NULL;
@@ -4500,11 +4494,6 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
}
-#ifdef _OPENMP
- if (!(sd->flags & SCULPT_USE_OPENMP))
- omp_set_num_threads(BLI_system_thread_count()); /* set back to original logical corecount */
-#endif
-
sculpt_brush_exit_tex(sd);
}
@@ -4800,6 +4789,7 @@ void sculpt_dynamic_topology_disable(bContext *C,
sculpt_update_after_dynamic_topology_toggle(C);
}
+
static int sculpt_dynamic_topology_toggle_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = CTX_data_active_object(C);
@@ -4820,15 +4810,50 @@ static int sculpt_dynamic_topology_toggle_exec(bContext *C, wmOperator *UNUSED(o
return OPERATOR_FINISHED;
}
+
+static int dyntopo_warning_popup(bContext *C, wmOperatorType *ot, bool vdata, bool modifiers)
+{
+ uiPopupMenu *pup = uiPupMenuBegin(C, IFACE_("Warning!"), ICON_ERROR);
+ uiLayout *layout = uiPupMenuLayout(pup);
+
+ if (vdata) {
+ const char *msg_error = TIP_("Vertex Data Detected!");
+ const char *msg = TIP_("Dyntopo will not preserve vertex colors, UVs, or other customdata");
+ uiItemL(layout, msg_error, ICON_INFO);
+ uiItemL(layout, msg, ICON_NONE);
+ uiItemS(layout);
+ }
+
+ if (modifiers) {
+ const char *msg_error = TIP_("Generative Modifiers Detected!");
+ const char *msg = TIP_("Keeping the modifiers will increase polycount when returning to object mode");
+
+ uiItemL(layout, msg_error, ICON_INFO);
+ uiItemL(layout, msg, ICON_NONE);
+ uiItemS(layout);
+ }
+
+ uiItemFullO_ptr(layout, ot, IFACE_("OK"), ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, 0);
+
+ uiPupMenuEnd(C, pup);
+
+ return OPERATOR_CANCELLED;
+}
+
+
static int sculpt_dynamic_topology_toggle_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
Object *ob = CTX_data_active_object(C);
Mesh *me = ob->data;
SculptSession *ss = ob->sculpt;
- const char *msg = TIP_("Dynamic-topology sculpting will not preserve vertex colors, UVs, or other customdata");
if (!ss->bm) {
+ Scene *scene = CTX_data_scene(C);
+ ModifierData *md;
+ VirtualModifierData virtualModifierData;
int i;
+ bool vdata = false;
+ bool modifiers = false;
for (i = 0; i < CD_NUMTYPES; i++) {
if (!ELEM7(i, CD_MVERT, CD_MEDGE, CD_MFACE, CD_MLOOP, CD_MPOLY, CD_PAINT_MASK, CD_ORIGINDEX) &&
@@ -4836,10 +4861,28 @@ static int sculpt_dynamic_topology_toggle_invoke(bContext *C, wmOperator *op, co
CustomData_has_layer(&me->edata, i) ||
CustomData_has_layer(&me->fdata, i)))
{
- /* The mesh has customdata that will be lost, let the user confirm this is OK */
- return WM_operator_confirm_message(C, op, msg);
+ vdata = true;
+ break;
}
}
+
+ md = modifiers_getVirtualModifierList(ob, &virtualModifierData);
+
+ /* exception for shape keys because we can edit those */
+ for (; md; md = md->next) {
+ ModifierTypeInfo *mti = modifierType_getInfo(md->type);
+ if (!modifier_isEnabled(scene, md, eModifierMode_Realtime)) continue;
+
+ if (mti->type == eModifierTypeType_Constructive) {
+ modifiers = true;
+ break;
+ }
+ }
+
+ if (vdata || modifiers) {
+ /* The mesh has customdata that will be lost, let the user confirm this is OK */
+ return dyntopo_warning_popup(C, op->type, vdata, modifiers);
+ }
}
return sculpt_dynamic_topology_toggle_exec(C, op);
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index fa398e3391b..0d49049c78e 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -62,7 +62,8 @@
#include "GPU_buffers.h"
-#include "ED_sculpt.h"
+#include "ED_paint.h"
+
#include "bmesh.h"
#include "paint_intern.h"
#include "sculpt_intern.h"
@@ -195,9 +196,9 @@ static int sculpt_undo_restore_hidden(bContext *C, DerivedMesh *dm,
for (i = 0; i < unode->totvert; i++) {
MVert *v = &mvert[unode->index[i]];
- int uval = BLI_BITMAP_GET(unode->vert_hidden, i);
+ int uval = BLI_BITMAP_TEST(unode->vert_hidden, i);
- BLI_BITMAP_MODIFY(unode->vert_hidden, i,
+ BLI_BITMAP_SET(unode->vert_hidden, i,
v->flag & ME_HIDE);
if (uval)
v->flag |= ME_HIDE;
@@ -702,7 +703,7 @@ static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode)
BKE_pbvh_node_num_verts(pbvh, node, NULL, &allvert);
BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert);
for (i = 0; i < allvert; i++) {
- BLI_BITMAP_MODIFY(unode->vert_hidden, i,
+ BLI_BITMAP_SET(unode->vert_hidden, i,
mvert[vert_indices[i]].flag & ME_HIDE);
}
}
diff --git a/source/blender/editors/space_action/action_ops.c b/source/blender/editors/space_action/action_ops.c
index 93cd94ed892..b99419dec20 100644
--- a/source/blender/editors/space_action/action_ops.c
+++ b/source/blender/editors/space_action/action_ops.c
@@ -224,6 +224,9 @@ static void action_keymap_keyframes(wmKeyConfig *keyconf, wmKeyMap *keymap)
*/
WM_keymap_add_item(keymap, "ANIM_OT_channels_editable_toggle", TABKEY, KM_PRESS, 0, 0);
+ /* find (i.e. a shortcut for setting the name filter) */
+ WM_keymap_add_item(keymap, "ANIM_OT_channels_find", FKEY, KM_PRESS, KM_CTRL, 0);
+
/* transform system */
transform_keymap_for_space(keyconf, keymap, SPACE_ACTION);
diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c
index 69eeac69e85..7ca8968a705 100644
--- a/source/blender/editors/space_action/space_action.c
+++ b/source/blender/editors/space_action/space_action.c
@@ -476,7 +476,14 @@ static void action_header_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa)
break;
case NC_ANIMATION:
switch (wmn->data) {
- case ND_KEYFRAME:
+ case ND_ANIMCHAN: /* set of visible animchannels changed */
+ /* NOTE: for now, this should usually just mean that the filters changed
+ * It may be better if we had a dedicated flag for that though
+ */
+ ED_region_tag_redraw(ar);
+ break;
+
+ case ND_KEYFRAME: /* new keyframed added -> active action may have changed */
//saction->flag |= SACTION_TEMP_NEEDCHANSYNC;
ED_region_tag_redraw(ar);
break;
diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index 83040a26480..7a74a58c69d 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -53,6 +53,7 @@
#include "ED_mesh.h"
#include "ED_node.h"
#include "ED_object.h"
+#include "ED_paint.h"
#include "ED_physics.h"
#include "ED_render.h"
#include "ED_screen.h"
diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c
index c558d811693..0aa3e47df4b 100644
--- a/source/blender/editors/space_buttons/buttons_texture.c
+++ b/source/blender/editors/space_buttons/buttons_texture.c
@@ -442,7 +442,7 @@ void buttons_texture_context_compute(const bContext *C, SpaceButs *sbuts)
set_texture_context(C, sbuts);
- if (!(BKE_scene_use_new_shading_nodes(scene) || (sbuts->texture_context == SB_TEXC_OTHER))) {
+ if (!((sbuts->texture_context == SB_TEXC_OTHER) || BKE_scene_use_new_shading_nodes(scene))) {
if (ct) {
BLI_freelistN(&ct->users);
MEM_freeN(ct);
diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c
index cda46d2a9c2..889613b5d13 100644
--- a/source/blender/editors/space_clip/clip_buttons.c
+++ b/source/blender/editors/space_clip/clip_buttons.c
@@ -84,8 +84,8 @@ void ED_clip_buttons_register(ARegionType *art)
strcpy(pt->idname, "CLIP_PT_gpencil");
strcpy(pt->label, N_("Grease Pencil"));
strcpy(pt->translation_context, BLF_I18NCONTEXT_DEFAULT_BPYRNA);
- pt->draw_header = gpencil_panel_standard_header;
- pt->draw = gpencil_panel_standard;
+ pt->draw_header = ED_gpencil_panel_standard_header;
+ pt->draw = ED_gpencil_panel_standard;
pt->flag |= PNL_DEFAULT_CLOSED;
pt->poll = clip_grease_pencil_panel_poll;
BLI_addtail(&art->paneltypes, pt);
diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c
index 9d413e81ea8..9a939bdc2ca 100644
--- a/source/blender/editors/space_clip/clip_draw.c
+++ b/source/blender/editors/space_clip/clip_draw.c
@@ -1795,12 +1795,12 @@ void clip_draw_grease_pencil(bContext *C, int onlyv2d)
}
}
- draw_gpencil_2dimage(C);
+ ED_gpencil_draw_2dimage(C);
glPopMatrix();
}
}
else {
- draw_gpencil_view2d(C, 0);
+ ED_gpencil_draw_view2d(C, 0);
}
}
diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c
index 4d9c262e1ff..860d9dc6b3c 100644
--- a/source/blender/editors/space_clip/tracking_select.c
+++ b/source/blender/editors/space_clip/tracking_select.c
@@ -792,7 +792,7 @@ void CLIP_OT_select_circle(wmOperatorType *ot)
/* properties */
RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
- RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "radius", 1, 1, INT_MAX, "Radius", "", 1, INT_MAX);
RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
}
diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c
index cfd82b67289..fbfa9358a22 100644
--- a/source/blender/editors/space_graph/graph_ops.c
+++ b/source/blender/editors/space_graph/graph_ops.c
@@ -441,8 +441,12 @@ void graphedit_keymap(wmKeyConfig *keyconf)
/* keymap for all regions */
keymap = WM_keymap_find(keyconf, "Graph Editor Generic", SPACE_IPO, 0);
WM_keymap_add_item(keymap, "GRAPH_OT_properties", NKEY, KM_PRESS, 0, 0);
+
/* extrapolation works on channels, not keys */
WM_keymap_add_item(keymap, "GRAPH_OT_extrapolation_type", EKEY, KM_PRESS, KM_SHIFT, 0);
+
+ /* find (i.e. a shortcut for setting the name filter) */
+ WM_keymap_add_item(keymap, "ANIM_OT_channels_find", FKEY, KM_PRESS, KM_CTRL, 0);
/* channels */
/* Channels are not directly handled by the Graph Editor module, but are inherited from the Animation module.
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index c2b9d92d00a..3675282654f 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -947,8 +947,8 @@ void image_buttons_register(ARegionType *art)
strcpy(pt->idname, "IMAGE_PT_gpencil");
strcpy(pt->label, N_("Grease Pencil"));
strcpy(pt->translation_context, BLF_I18NCONTEXT_DEFAULT_BPYRNA);
- pt->draw_header = gpencil_panel_standard_header;
- pt->draw = gpencil_panel_standard;
+ pt->draw_header = ED_gpencil_panel_standard_header;
+ pt->draw = ED_gpencil_panel_standard;
BLI_strncpy(pt->category, category, BLI_strlen_utf8(category));
BLI_addtail(&art->paneltypes, pt);
}
diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c
index 016143f640b..5f996f94a81 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -644,14 +644,14 @@ void draw_image_grease_pencil(bContext *C, bool onlyv2d)
/* draw in View2D space? */
if (onlyv2d) {
/* draw grease-pencil ('image' strokes) */
- draw_gpencil_2dimage(C);
+ ED_gpencil_draw_2dimage(C);
}
else {
/* assume that UI_view2d_restore(C) has been called... */
//SpaceImage *sima = (SpaceImage *)CTX_wm_space_data(C);
/* draw grease-pencil ('screen' strokes) */
- draw_gpencil_view2d(C, 0);
+ ED_gpencil_draw_view2d(C, 0);
}
}
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index f7a4308c167..cc769f13b82 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -72,6 +72,7 @@
#include "RNA_enum_types.h"
#include "ED_image.h"
+#include "ED_paint.h"
#include "ED_render.h"
#include "ED_screen.h"
#include "ED_space_api.h"
@@ -88,7 +89,6 @@
#include "PIL_time.h"
#include "image_intern.h"
-#include "ED_sculpt.h"
/******************** view navigation utilities *********************/
@@ -1003,7 +1003,7 @@ static int image_cmp_frame(void *a, void *b)
* \brief Return the start (offset) and the length of the sequence of continuous frames in the list of frames
* \param frames [in] the list of frame numbers, as a side-effect the list is sorted
* \param ofs [out] offest, the first frame number in the sequence
- * \return the number of continuos frames in the sequence
+ * \return the number of contiguous frames in the sequence
*/
static int image_sequence_get_len(ListBase *frames, int *ofs)
{
@@ -1040,7 +1040,11 @@ static int image_open_exec(bContext *C, wmOperator *op)
const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path");
- if (RNA_struct_property_is_set(op->ptr, "files") && RNA_struct_property_is_set(op->ptr, "directory")) {
+ RNA_string_get(op->ptr, "filepath", path);
+
+ if (!IMB_isanim(path) && RNA_struct_property_is_set(op->ptr, "files") &&
+ RNA_struct_property_is_set(op->ptr, "directory"))
+ {
ListBase frames;
BLI_listbase_clear(&frames);
@@ -1048,9 +1052,6 @@ static int image_open_exec(bContext *C, wmOperator *op)
frame_seq_len = image_sequence_get_len(&frames, &frame_ofs);
BLI_freelistN(&frames);
}
- else {
- RNA_string_get(op->ptr, "filepath", path);
- }
errno = 0;
diff --git a/source/blender/editors/space_logic/logic_window.c b/source/blender/editors/space_logic/logic_window.c
index 4b533292a95..b52d6265800 100644
--- a/source/blender/editors/space_logic/logic_window.c
+++ b/source/blender/editors/space_logic/logic_window.c
@@ -519,6 +519,8 @@ static const char *actuator_name(int type)
return N_("Armature");
case ACT_STEERING:
return N_("Steering");
+ case ACT_MOUSE:
+ return N_("Mouse");
}
return N_("Unknown");
}
@@ -1182,9 +1184,9 @@ static void draw_sensor_property(uiLayout *layout, PointerRNA *ptr)
uiItemR(row, ptr, "value_max", 0, NULL, ICON_NONE);
break;
case SENS_PROP_EQUAL:
- uiItemR(layout, ptr, "value", 0, NULL, ICON_NONE);
- break;
case SENS_PROP_NEQUAL:
+ case SENS_PROP_LESSTHAN:
+ case SENS_PROP_GREATERTHAN:
uiItemR(layout, ptr, "value", 0, NULL, ICON_NONE);
break;
case SENS_PROP_CHANGED:
@@ -2177,6 +2179,68 @@ static void draw_actuator_steering(uiLayout *layout, PointerRNA *ptr)
}
}
+static void draw_actuator_mouse(uiLayout *layout, PointerRNA *ptr)
+{
+ uiLayout *row, *col, *subcol, *split, *subsplit;
+
+ uiItemR(layout, ptr, "mode", 0, NULL, 0);
+
+ switch (RNA_enum_get(ptr, "mode")) {
+ case ACT_MOUSE_VISIBILITY:
+ row = uiLayoutRow(layout, 0);
+ uiItemR(row, ptr, "visible", UI_ITEM_R_TOGGLE, NULL, 0);
+ break;
+
+ case ACT_MOUSE_LOOK:
+ /* X axis */
+ row = uiLayoutRow(layout, 0);
+ col = uiLayoutColumn(row, 1);
+
+ uiItemR(col, ptr, "use_axis_x", UI_ITEM_R_TOGGLE, NULL, 0);
+
+ subcol = uiLayoutColumn(col, 1);
+ uiLayoutSetActive(subcol, RNA_boolean_get(ptr, "use_axis_x")==1);
+ uiItemR(subcol, ptr, "sensitivity_x", 0, NULL, 0);
+ uiItemR(subcol, ptr, "threshold_x", 0, NULL, 0);
+
+ uiItemR(subcol, ptr, "min_x", 0, NULL, 0);
+ uiItemR(subcol, ptr, "max_x", 0, NULL, 0);
+
+ uiItemR(subcol, ptr, "object_axis_x", 0, NULL, 0);
+
+ /* Y Axis */
+ col = uiLayoutColumn(row, 1);
+
+ uiItemR(col, ptr, "use_axis_y", UI_ITEM_R_TOGGLE, NULL, 0);
+
+ subcol = uiLayoutColumn(col, 1);
+ uiLayoutSetActive(subcol, RNA_boolean_get(ptr, "use_axis_y")==1);
+ uiItemR(subcol, ptr, "sensitivity_y", 0, NULL, 0);
+ uiItemR(subcol, ptr, "threshold_y", 0, NULL, 0);
+
+ uiItemR(subcol, ptr, "min_y", 0, NULL, 0);
+ uiItemR(subcol, ptr, "max_y", 0, NULL, 0);
+
+ uiItemR(subcol, ptr, "object_axis_y", 0, NULL, 0);
+
+ /* Lower options */
+ row = uiLayoutRow(layout, 0);
+ split = uiLayoutSplit(row, 0.5, 0);
+
+ subsplit = uiLayoutSplit(split, 0.5, 1);
+ uiLayoutSetActive(subsplit, RNA_boolean_get(ptr, "use_axis_x")==1);
+ uiItemR(subsplit, ptr, "local_x", UI_ITEM_R_TOGGLE, NULL, 0);
+ uiItemR(subsplit, ptr, "reset_x", UI_ITEM_R_TOGGLE, NULL, 0);
+
+ subsplit = uiLayoutSplit(split, 0.5, 1);
+ uiLayoutSetActive(subsplit, RNA_boolean_get(ptr, "use_axis_y")==1);
+ uiItemR(subsplit, ptr, "local_y", UI_ITEM_R_TOGGLE, NULL, 0);
+ uiItemR(subsplit, ptr, "reset_y", UI_ITEM_R_TOGGLE, NULL, 0);
+
+ break;
+ }
+}
+
static void draw_brick_actuator(uiLayout *layout, PointerRNA *ptr, bContext *C)
{
uiLayout *box;
@@ -2241,6 +2305,10 @@ static void draw_brick_actuator(uiLayout *layout, PointerRNA *ptr, bContext *C)
break;
case ACT_STEERING:
draw_actuator_steering(box, ptr);
+ break;
+ case ACT_MOUSE:
+ draw_actuator_mouse(box, ptr);
+ break;
}
}
diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c
index 295a7ab2e04..5e1381db696 100644
--- a/source/blender/editors/space_nla/nla_ops.c
+++ b/source/blender/editors/space_nla/nla_ops.c
@@ -323,6 +323,9 @@ void nla_keymap(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "NLA_OT_tweakmode_enter", TABKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "NLA_OT_tweakmode_exit", TABKEY, KM_PRESS, 0, 0);
+ /* find (i.e. a shortcut for setting the name filter) */
+ WM_keymap_add_item(keymap, "ANIM_OT_channels_find", FKEY, KM_PRESS, KM_CTRL, 0);
+
/* channels ---------------------------------------------------------- */
/* Channels are not directly handled by the NLA Editor module, but are inherited from the Animation module.
* Most of the relevant operations, keymaps, drawing, etc. can therefore all be found in that module instead, as there
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index 953777ff329..4e709046f45 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -930,6 +930,11 @@ static void node_shader_buts_glossy(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(layout, ptr, "distribution", 0, "", ICON_NONE);
}
+static void node_shader_buts_anisotropic(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "distribution", 0, "", ICON_NONE);
+}
+
static void node_shader_buts_subsurface(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
/* SSS does not work on GPU yet */
@@ -1096,6 +1101,9 @@ static void node_shader_set_butfunc(bNodeType *ntype)
case SH_NODE_BSDF_REFRACTION:
ntype->draw_buttons = node_shader_buts_glossy;
break;
+ case SH_NODE_BSDF_ANISOTROPIC:
+ ntype->draw_buttons = node_shader_buts_anisotropic;
+ break;
case SH_NODE_SUBSURFACE_SCATTERING:
ntype->draw_buttons = node_shader_buts_subsurface;
break;
diff --git a/source/blender/editors/space_node/node_buttons.c b/source/blender/editors/space_node/node_buttons.c
index ebff840dc9f..58d94a28226 100644
--- a/source/blender/editors/space_node/node_buttons.c
+++ b/source/blender/editors/space_node/node_buttons.c
@@ -202,8 +202,8 @@ void node_buttons_register(ARegionType *art)
strcpy(pt->idname, "NODE_PT_gpencil");
strcpy(pt->label, N_("Grease Pencil"));
strcpy(pt->translation_context, BLF_I18NCONTEXT_DEFAULT_BPYRNA);
- pt->draw_header = gpencil_panel_standard_header;
- pt->draw = gpencil_panel_standard;
+ pt->draw_header = ED_gpencil_panel_standard_header;
+ pt->draw = ED_gpencil_panel_standard;
pt->poll = active_nodetree_poll;
BLI_addtail(&art->paneltypes, pt);
}
diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c
index b3cf5ce0c81..1d27ecb30b7 100644
--- a/source/blender/editors/space_node/node_draw.c
+++ b/source/blender/editors/space_node/node_draw.c
@@ -1341,7 +1341,7 @@ void drawnodespace(const bContext *C, ARegion *ar)
if (snode->flag & SNODE_SHOW_GPENCIL) {
/* draw grease-pencil ('canvas' strokes) */
- draw_gpencil_view2d(C, true);
+ ED_gpencil_draw_view2d(C, true);
}
}
else {
@@ -1360,7 +1360,7 @@ void drawnodespace(const bContext *C, ARegion *ar)
if (snode->treepath.last) {
if (snode->flag & SNODE_SHOW_GPENCIL) {
/* draw grease-pencil (screen strokes, and also paintbuffer) */
- draw_gpencil_view2d(C, false);
+ ED_gpencil_draw_view2d(C, false);
}
}
diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c
index 7371a3ef070..434a9e11854 100644
--- a/source/blender/editors/space_node/node_select.c
+++ b/source/blender/editors/space_node/node_select.c
@@ -563,7 +563,7 @@ void NODE_OT_select_circle(wmOperatorType *ot)
/* rna */
RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
- RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "radius", 1, 1, INT_MAX, "Radius", "", 1, INT_MAX);
RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
}
@@ -971,7 +971,6 @@ static uiBlock *node_find_menu(bContext *C, ARegion *ar, void *arg_op)
uiDefBut(block, LABEL, 0, "", 10, 10 - uiSearchBoxHeight(), uiSearchBoxWidth(), uiSearchBoxHeight(), NULL, 0, 0, 0, 0, NULL);
uiPopupBoundsBlock(block, 6, 0, -UI_UNIT_Y); /* move it downwards, mouse over button */
- uiEndBlock(C, block);
// uiButActiveOnly(C, ar, block, but); XXX using this here makes Blender hang - investigate
wm_event_init_from_window(win, &event);
diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.c
index 44639b1295c..99a481e5d82 100644
--- a/source/blender/editors/space_node/node_templates.c
+++ b/source/blender/editors/space_node/node_templates.c
@@ -511,6 +511,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_
bNodeSocket *sock = arg->sock;
bNodeTreeType *ntreetype = arg->ntree->typeinfo;
+ uiBlockSetFlag(block, UI_BLOCK_NO_FLIP);
uiBlockSetCurLayout(block, layout);
split = uiLayoutSplit(layout, 0.0f, false);
diff --git a/source/blender/editors/space_sequencer/sequencer_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c
index 36589984c78..d75eeca2c67 100644
--- a/source/blender/editors/space_sequencer/sequencer_buttons.c
+++ b/source/blender/editors/space_sequencer/sequencer_buttons.c
@@ -68,8 +68,8 @@ void sequencer_buttons_register(ARegionType *art)
strcpy(pt->idname, "SEQUENCER_PT_gpencil");
strcpy(pt->label, N_("Grease Pencil"));
strcpy(pt->translation_context, BLF_I18NCONTEXT_DEFAULT_BPYRNA);
- pt->draw_header = gpencil_panel_standard_header;
- pt->draw = gpencil_panel_standard;
+ pt->draw_header = ED_gpencil_panel_standard_header;
+ pt->draw = ED_gpencil_panel_standard;
pt->poll = sequencer_grease_pencil_panel_poll;
BLI_addtail(&art->paneltypes, pt);
}
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 4ecda80ab66..4ad5ed8bb80 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -1236,7 +1236,7 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq
if (sseq->flag & SEQ_SHOW_GPENCIL) {
if (is_imbuf) {
/* draw grease-pencil (image aligned) */
- draw_gpencil_2dimage(C);
+ ED_gpencil_draw_2dimage(C);
}
}
@@ -1249,7 +1249,7 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq
if (sseq->flag & SEQ_SHOW_GPENCIL) {
if (is_imbuf) {
/* draw grease-pencil (screen aligned) */
- draw_gpencil_view2d(C, 0);
+ ED_gpencil_draw_view2d(C, 0);
}
}
diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c
index 6e49569314f..fbca9bcc04f 100644
--- a/source/blender/editors/space_text/space_text.c
+++ b/source/blender/editors/space_text/space_text.c
@@ -484,7 +484,13 @@ static int text_drop_paste_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent
static void text_drop_paste(wmDrag *drag, wmDropBox *drop)
{
- RNA_string_set(drop->ptr, "text", ((ID *)drag->poin)->name + 2);
+ char *text;
+ ID *id = drag->poin;
+
+ /* copy drag path to properties */
+ text = RNA_path_full_ID_py(id);
+ RNA_string_set(drop->ptr, "text", text);
+ MEM_freeN(text);
}
/* this region dropbox definition */
diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c
index 03f2fa86ecb..de4011c9349 100644
--- a/source/blender/editors/space_view3d/drawmesh.c
+++ b/source/blender/editors/space_view3d/drawmesh.c
@@ -113,8 +113,8 @@ static BLI_bitmap *get_tface_mesh_marked_edge_info(Mesh *me)
ml = me->mloop + mp->loopstart;
for (j = 0; j < mp->totloop; j++, ml++) {
- BLI_BITMAP_SET(bitmap_edge_flags, edge_vis_index(ml->e));
- if (select_set) BLI_BITMAP_SET(bitmap_edge_flags, edge_sel_index(ml->e));
+ BLI_BITMAP_ENABLE(bitmap_edge_flags, edge_vis_index(ml->e));
+ if (select_set) BLI_BITMAP_ENABLE(bitmap_edge_flags, edge_sel_index(ml->e));
}
}
}
@@ -129,12 +129,12 @@ static DMDrawOption draw_mesh_face_select__setHiddenOpts(void *userData, int ind
Mesh *me = data->me;
if (me->drawflag & ME_DRAWEDGES) {
- if ((me->drawflag & ME_HIDDENEDGES) || (BLI_BITMAP_GET(data->edge_flags, edge_vis_index(index))))
+ if ((me->drawflag & ME_HIDDENEDGES) || (BLI_BITMAP_TEST(data->edge_flags, edge_vis_index(index))))
return DM_DRAW_OPTION_NORMAL;
else
return DM_DRAW_OPTION_SKIP;
}
- else if (BLI_BITMAP_GET(data->edge_flags, edge_sel_index(index)))
+ else if (BLI_BITMAP_TEST(data->edge_flags, edge_sel_index(index)))
return DM_DRAW_OPTION_NORMAL;
else
return DM_DRAW_OPTION_SKIP;
@@ -143,7 +143,7 @@ static DMDrawOption draw_mesh_face_select__setHiddenOpts(void *userData, int ind
static DMDrawOption draw_mesh_face_select__setSelectOpts(void *userData, int index)
{
drawMeshFaceSelect_userData *data = userData;
- return (BLI_BITMAP_GET(data->edge_flags, edge_sel_index(index))) ? DM_DRAW_OPTION_NORMAL : DM_DRAW_OPTION_SKIP;
+ return (BLI_BITMAP_TEST(data->edge_flags, edge_sel_index(index))) ? DM_DRAW_OPTION_NORMAL : DM_DRAW_OPTION_SKIP;
}
/* draws unselected */
@@ -947,7 +947,10 @@ void draw_mesh_textured(Scene *scene, View3D *v3d, RegionView3D *rv3d,
Object *ob, DerivedMesh *dm, const int draw_flags)
{
/* if not cycles, or preview-modifiers, or drawing matcaps */
- if ((!BKE_scene_use_new_shading_nodes(scene)) || (draw_flags & DRAW_MODIFIERS_PREVIEW) || (v3d->flag2 & V3D_SHOW_SOLID_MATCAP)) {
+ if ((draw_flags & DRAW_MODIFIERS_PREVIEW) ||
+ (v3d->flag2 & V3D_SHOW_SOLID_MATCAP) ||
+ (BKE_scene_use_new_shading_nodes(scene) == false))
+ {
draw_mesh_textured_old(scene, v3d, rv3d, ob, dm, draw_flags);
return;
}
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index 2f3c416d335..0e010eff94d 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -152,6 +152,17 @@ typedef struct drawDMEdgesSelInterp_userData {
unsigned char *lastCol;
} drawDMEdgesSelInterp_userData;
+typedef struct drawDMEdgesWeightInterp_userData {
+ BMesh *bm;
+
+ int cd_dvert_offset;
+ int defgroup_tot;
+ int vgroup_index;
+ char weight_user;
+ float alert_color[3];
+
+} drawDMEdgesWeightInterp_userData;
+
typedef struct drawDMFacesSel_userData {
#ifdef WITH_FREESTYLE
unsigned char *cols[4];
@@ -175,16 +186,26 @@ typedef struct drawDMNormal_userData {
float imat[3][3];
} drawDMNormal_userData;
-typedef struct bbsObmodeMeshVerts_userData {
- void *offset;
+typedef struct drawMVertOffset_userData {
MVert *mvert;
-} bbsObmodeMeshVerts_userData;
+ int offset;
+} drawMVertOffset_userData;
typedef struct drawDMLayer_userData {
BMesh *bm;
int cd_layer_offset;
} drawDMLayer_userData;
+typedef struct drawBMOffset_userData {
+ BMesh *bm;
+ int offset;
+} drawBMOffset_userData;
+
+typedef struct drawBMSelect_userData {
+ BMesh *bm;
+ bool select;
+} drawBMSelect_userData;
+
static void draw_bounding_volume(Object *ob, char type);
static void drawcube_size(float size);
@@ -763,8 +784,10 @@ typedef struct ViewCachedString {
short sco[2];
short xoffs;
short flag;
- int str_len, pad;
+ int str_len;
+
/* str is allocated past the end */
+ char str[0];
} ViewCachedString;
/* one arena for all 3 string lists */
@@ -789,7 +812,6 @@ void view3d_cached_text_draw_add(const float co[3],
const unsigned char col[4])
{
int alloc_len = str_len + 1;
- /* TODO, replace with more efficient malloc, perhaps memarena per draw? */
ViewCachedString *vos;
BLI_assert(str_len == strlen(str));
@@ -809,7 +831,7 @@ void view3d_cached_text_draw_add(const float co[3],
vos->str_len = str_len;
/* allocate past the end */
- memcpy(vos + 1, str, alloc_len);
+ memcpy(vos->str, str, alloc_len);
}
void view3d_cached_text_draw_end(View3D *v3d, ARegion *ar, bool depth_write, float mat[4][4])
@@ -868,8 +890,6 @@ void view3d_cached_text_draw_end(View3D *v3d, ARegion *ar, bool depth_write, flo
for (vos = g_v3d_strings[g_v3d_string_level]; vos; vos = vos->next) {
if (vos->sco[0] != IS_CLIPPED) {
- const char *str = (char *)(vos + 1);
-
if (col_pack_prev != vos->col.pack) {
glColor3ubv(vos->col.ub);
col_pack_prev = vos->col.pack;
@@ -878,10 +898,10 @@ void view3d_cached_text_draw_end(View3D *v3d, ARegion *ar, bool depth_write, flo
((vos->flag & V3D_CACHE_TEXT_ASCII) ?
BLF_draw_default_ascii :
BLF_draw_default
- )( (float)vos->sco[0] + vos->xoffs,
- (float)vos->sco[1],
+ )((float)(vos->sco[0] + vos->xoffs),
+ (float)(vos->sco[1]),
(depth_write) ? 0.0f : 2.0f,
- str,
+ vos->str,
vos->str_len);
}
}
@@ -2174,21 +2194,21 @@ static void draw_dm_face_normals(BMEditMesh *em, Scene *scene, Object *ob, Deriv
static void draw_dm_face_centers__mapFunc(void *userData, int index, const float cent[3], const float UNUSED(no[3]))
{
- BMFace *efa = BM_face_at_index(((void **)userData)[0], index);
- const char sel = *(((char **)userData)[1]);
+ drawBMSelect_userData *data = userData;
+ BMFace *efa = BM_face_at_index(data->bm, index);
if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) &&
- (BM_elem_flag_test(efa, BM_ELEM_SELECT) == sel))
+ (BM_elem_flag_test(efa, BM_ELEM_SELECT) == data->select))
{
bglVertex3fv(cent);
}
}
-static void draw_dm_face_centers(BMEditMesh *em, DerivedMesh *dm, char sel)
+static void draw_dm_face_centers(BMEditMesh *em, DerivedMesh *dm, bool select)
{
- void *ptrs[2] = {em->bm, &sel};
+ drawBMSelect_userData data = {em->bm, select};
bglBegin(GL_POINTS);
- dm->foreachMappedFaceCenter(dm, draw_dm_face_centers__mapFunc, ptrs, DM_FOREACH_NOP);
+ dm->foreachMappedFaceCenter(dm, draw_dm_face_centers__mapFunc, &data, DM_FOREACH_NOP);
bglEnd();
}
@@ -2371,7 +2391,8 @@ static void draw_dm_edges(BMEditMesh *em, DerivedMesh *dm)
/* Draw edges with color interpolated based on selection */
static DMDrawOption draw_dm_edges_sel_interp__setDrawOptions(void *userData, int index)
{
- if (BM_elem_flag_test(BM_edge_at_index(((void **)userData)[0], index), BM_ELEM_HIDDEN))
+ drawDMEdgesSelInterp_userData *data = userData;
+ if (BM_elem_flag_test(BM_edge_at_index(data->bm, index), BM_ELEM_HIDDEN))
return DM_DRAW_OPTION_SKIP;
else
return DM_DRAW_OPTION_NORMAL;
@@ -2379,7 +2400,7 @@ static DMDrawOption draw_dm_edges_sel_interp__setDrawOptions(void *userData, int
static void draw_dm_edges_sel_interp__setDrawInterpOptions(void *userData, int index, float t)
{
drawDMEdgesSelInterp_userData *data = userData;
- BMEdge *eed = BM_edge_at_index(((void **)userData)[0], index);
+ BMEdge *eed = BM_edge_at_index(data->bm, index);
unsigned char **cols = userData;
unsigned int col0_id = (BM_elem_flag_test(eed->v1, BM_ELEM_SELECT)) ? 2 : 1;
unsigned int col1_id = (BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) ? 2 : 1;
@@ -2421,6 +2442,106 @@ static void draw_dm_edges_sel_interp(BMEditMesh *em, DerivedMesh *dm, unsigned c
dm->drawMappedEdgesInterp(dm, draw_dm_edges_sel_interp__setDrawOptions, draw_dm_edges_sel_interp__setDrawInterpOptions, &data);
}
+static void bm_color_from_weight(float col[3], BMVert *vert, drawDMEdgesWeightInterp_userData *data)
+{
+ MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(vert, data->cd_dvert_offset);
+ float weight = defvert_find_weight(dvert, data->vgroup_index);
+
+ if ((weight == 0.0f) &&
+ ((data->weight_user == OB_DRAW_GROUPUSER_ACTIVE) ||
+ ((data->weight_user == OB_DRAW_GROUPUSER_ALL) && defvert_is_weight_zero(dvert, data->defgroup_tot))))
+ {
+ copy_v3_v3(col, data->alert_color);
+ }
+ else {
+ weight_to_rgb(col, weight);
+ }
+}
+
+static void draw_dm_edges_nop_interp__setDrawInterpOptions(void *UNUSED(userData), int UNUSED(index), float UNUSED(t))
+{
+ /* pass */
+}
+
+static void draw_dm_edges_weight_interp__setDrawInterpOptions(void *userData, int index, float t)
+{
+ drawDMEdgesWeightInterp_userData *data = userData;
+ BMEdge *eed = BM_edge_at_index(data->bm, index);
+ float col[3];
+
+ if (t == 0.0f) {
+ bm_color_from_weight(col, eed->v1, data);
+ }
+ else if (t == 1.0f) {
+ bm_color_from_weight(col, eed->v2, data);
+ }
+ else {
+ float col_v1[3];
+ float col_v2[3];
+
+ bm_color_from_weight(col_v1, eed->v1, data);
+ bm_color_from_weight(col_v2, eed->v2, data);
+ interp_v3_v3v3(col, col_v1, col_v2, t);
+ }
+
+ glColor3fv(col);
+}
+
+static void draw_dm_edges_weight_interp(BMEditMesh *em, DerivedMesh *dm, const char weight_user)
+{
+ drawDMEdgesWeightInterp_userData data;
+ Object *ob = em->ob;
+
+ data.bm = em->bm;
+ data.cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT);
+ data.defgroup_tot = BLI_countlist(&ob->defbase);
+ data.vgroup_index = ob->actdef - 1;
+ data.weight_user = weight_user;
+ UI_GetThemeColor3fv(TH_VERTEX_UNREFERENCED, data.alert_color);
+
+ if ((data.vgroup_index != -1) && (data.cd_dvert_offset != -1)) {
+ glEnable(GL_BLEND);
+ dm->drawMappedEdgesInterp(
+ dm,
+ draw_dm_edges_sel_interp__setDrawOptions,
+ draw_dm_edges_weight_interp__setDrawInterpOptions,
+ &data);
+ glDisable(GL_BLEND);
+ }
+ else {
+ float col[3];
+
+ if (data.weight_user == OB_DRAW_GROUPUSER_NONE) {
+ weight_to_rgb(col, 0.0f);
+ }
+ else {
+ copy_v3_v3(col, data.alert_color);
+ }
+ glColor3fv(col);
+
+ dm->drawMappedEdgesInterp(
+ dm,
+ draw_dm_edges_sel_interp__setDrawOptions,
+ draw_dm_edges_nop_interp__setDrawInterpOptions,
+ &data);
+ }
+
+}
+
+static bool draw_dm_edges_weight_check(Mesh *me, View3D *v3d)
+{
+ if (me->drawflag & ME_DRAWEIGHT) {
+ if ((v3d->drawtype == OB_WIRE) ||
+ (v3d->flag2 & V3D_SOLID_MATCAP) ||
+ ((v3d->flag2 & V3D_OCCLUDE_WIRE) && (v3d->drawtype > OB_WIRE)))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
/* Draw only seam edges */
static DMDrawOption draw_dm_edges_seams__setDrawOptions(void *userData, int index)
{
@@ -2833,9 +2954,16 @@ static void draw_em_fancy_edges(BMEditMesh *em, Scene *scene, View3D *v3d,
draw_dm_edges_sel(em, cageDM, wireCol, selCol, actCol, eed_act);
}
else if ((me->drawflag & ME_DRAWEDGES) || (ts->selectmode & SCE_SELECT_EDGE)) {
- if (cageDM->drawMappedEdgesInterp && (ts->selectmode & SCE_SELECT_VERTEX)) {
+ if (cageDM->drawMappedEdgesInterp &&
+ ((ts->selectmode & SCE_SELECT_VERTEX) || (me->drawflag & ME_DRAWEIGHT)))
+ {
glShadeModel(GL_SMOOTH);
- draw_dm_edges_sel_interp(em, cageDM, wireCol, selCol);
+ if (draw_dm_edges_weight_check(me, v3d)) {
+ draw_dm_edges_weight_interp(em, cageDM, ts->weightuser);
+ }
+ else {
+ draw_dm_edges_sel_interp(em, cageDM, wireCol, selCol);
+ }
glShadeModel(GL_FLAT);
}
else {
@@ -3622,7 +3750,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D
if (ob->sculpt->partial_redraw) {
if (ar->do_draw & RGN_DRAW_PARTIAL) {
- sculpt_get_redraw_planes(planes, ar, rv3d, ob);
+ ED_sculpt_redraw_planes_get(planes, ar, rv3d, ob);
fpl = planes;
ob->sculpt->partial_redraw = 0;
}
@@ -3720,7 +3848,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D
if (ob->sculpt->partial_redraw) {
if (ar->do_draw & RGN_DRAW_PARTIAL) {
- sculpt_get_redraw_planes(planes, ar, rv3d, ob);
+ ED_sculpt_redraw_planes_get(planes, ar, rv3d, ob);
fpl = planes;
ob->sculpt->partial_redraw = 0;
}
@@ -3870,12 +3998,14 @@ static bool draw_mesh_object(Scene *scene, ARegion *ar, View3D *v3d, RegionView3
else {
/* ob->bb was set by derived mesh system, do NULL check just to be sure */
if (me->totpoly <= 4 || (!ob->bb || ED_view3d_boundbox_clip(rv3d, ob->bb))) {
- const bool glsl = draw_glsl_material(scene, ob, v3d, dt);
- const bool check_alpha = check_alpha_pass(base);
+ if (dt > OB_WIRE) {
+ const bool glsl = draw_glsl_material(scene, ob, v3d, dt);
- if (dt == OB_SOLID || glsl) {
- GPU_begin_object_materials(v3d, rv3d, scene, ob, glsl,
- (check_alpha) ? &do_alpha_after : NULL);
+ if (dt == OB_SOLID || glsl) {
+ const bool check_alpha = check_alpha_pass(base);
+ GPU_begin_object_materials(v3d, rv3d, scene, ob, glsl,
+ (check_alpha) ? &do_alpha_after : NULL);
+ }
}
draw_mesh_fancy(scene, ar, v3d, rv3d, base, dt, ob_wire_col, dflag);
@@ -5914,7 +6044,7 @@ static void drawnurb(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base,
UI_ThemeColor(TH_WIRE_EDIT);
for (bl = ob->curve_cache->bev.first, nu = nurb; nu && bl; bl = bl->next, nu = nu->next) {
- BevPoint *bevp = (BevPoint *)(bl + 1);
+ BevPoint *bevp = bl->bevpoints;
int nr = bl->nr;
int skip = nu->resolu / 16;
@@ -5960,6 +6090,149 @@ static void drawnurb(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base,
if (v3d->zbuf) glDepthFunc(GL_LEQUAL);
}
+static void draw_editfont_textcurs(RegionView3D *rv3d, float textcurs[4][2])
+{
+ cpack(0);
+ ED_view3d_polygon_offset(rv3d, -1.0);
+ set_inverted_drawing(1);
+ glBegin(GL_QUADS);
+ glVertex2fv(textcurs[0]);
+ glVertex2fv(textcurs[1]);
+ glVertex2fv(textcurs[2]);
+ glVertex2fv(textcurs[3]);
+ glEnd();
+ set_inverted_drawing(0);
+ ED_view3d_polygon_offset(rv3d, 0.0);
+}
+
+static void draw_editfont(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base,
+ const char dt, const short dflag, const unsigned char ob_wire_col[4])
+{
+ Object *ob = base->object;
+ Curve *cu = ob->data;
+ EditFont *ef = cu->editfont;
+ float vec1[3], vec2[3];
+ int i, selstart, selend;
+
+ draw_editfont_textcurs(rv3d, ef->textcurs);
+
+ if (cu->flag & CU_FAST) {
+ cpack(0xFFFFFF);
+ set_inverted_drawing(1);
+ drawDispList(scene, v3d, rv3d, base, OB_WIRE, dflag, ob_wire_col);
+ set_inverted_drawing(0);
+ }
+ else {
+ drawDispList(scene, v3d, rv3d, base, dt, dflag, ob_wire_col);
+ }
+
+ if (cu->linewidth != 0.0f) {
+ UI_ThemeColor(TH_WIRE_EDIT);
+ copy_v3_v3(vec1, ob->orig);
+ copy_v3_v3(vec2, ob->orig);
+ vec1[0] += cu->linewidth;
+ vec2[0] += cu->linewidth;
+ vec1[1] += cu->linedist * cu->fsize;
+ vec2[1] -= cu->lines * cu->linedist * cu->fsize;
+ setlinestyle(3);
+ glBegin(GL_LINE_STRIP);
+ glVertex2fv(vec1);
+ glVertex2fv(vec2);
+ glEnd();
+ setlinestyle(0);
+ }
+
+ setlinestyle(3);
+ for (i = 0; i < cu->totbox; i++) {
+ if (cu->tb[i].w != 0.0f) {
+ UI_ThemeColor(i == (cu->actbox - 1) ? TH_ACTIVE : TH_WIRE);
+ vec1[0] = cu->xof + cu->tb[i].x;
+ vec1[1] = cu->yof + cu->tb[i].y + cu->fsize;
+ vec1[2] = 0.001;
+ glBegin(GL_LINE_STRIP);
+ glVertex3fv(vec1);
+ vec1[0] += cu->tb[i].w;
+ glVertex3fv(vec1);
+ vec1[1] -= cu->tb[i].h;
+ glVertex3fv(vec1);
+ vec1[0] -= cu->tb[i].w;
+ glVertex3fv(vec1);
+ vec1[1] += cu->tb[i].h;
+ glVertex3fv(vec1);
+ glEnd();
+ }
+ }
+ setlinestyle(0);
+
+
+ if (BKE_vfont_select_get(ob, &selstart, &selend) && ef->selboxes) {
+ const int seltot = selend - selstart;
+ float selboxw;
+
+ cpack(0xffffff);
+ set_inverted_drawing(1);
+ for (i = 0; i <= seltot; i++) {
+ EditFontSelBox *sb = &ef->selboxes[i];
+ float tvec[3];
+
+ if (i != seltot) {
+ if (ef->selboxes[i + 1].y == sb->y)
+ selboxw = ef->selboxes[i + 1].x - sb->x;
+ else
+ selboxw = sb->w;
+ }
+ else {
+ selboxw = sb->w;
+ }
+
+ /* fill in xy below */
+ tvec[2] = 0.001;
+
+ glBegin(GL_QUADS);
+
+ if (sb->rot == 0.0f) {
+ copy_v2_fl2(tvec, sb->x, sb->y);
+ glVertex3fv(tvec);
+
+ copy_v2_fl2(tvec, sb->x + selboxw, sb->y);
+ glVertex3fv(tvec);
+
+ copy_v2_fl2(tvec, sb->x + selboxw, sb->y + sb->h);
+ glVertex3fv(tvec);
+
+ copy_v2_fl2(tvec, sb->x, sb->y + sb->h);
+ glVertex3fv(tvec);
+ }
+ else {
+ float mat[2][2];
+
+ angle_to_mat2(mat, sb->rot);
+
+ copy_v2_fl2(tvec, sb->x, sb->y);
+ glVertex3fv(tvec);
+
+ copy_v2_fl2(tvec, selboxw, 0.0f);
+ mul_m2v2(mat, tvec);
+ add_v2_v2(tvec, &sb->x);
+ glVertex3fv(tvec);
+
+ copy_v2_fl2(tvec, selboxw, sb->h);
+ mul_m2v2(mat, tvec);
+ add_v2_v2(tvec, &sb->x);
+ glVertex3fv(tvec);
+
+ copy_v2_fl2(tvec, 0.0f, sb->h);
+ mul_m2v2(mat, tvec);
+ add_v2_v2(tvec, &sb->x);
+ glVertex3fv(tvec);
+ }
+
+ glEnd();
+ }
+ set_inverted_drawing(0);
+ }
+}
+
/* draw a sphere for use as an empty drawtype */
static void draw_empty_sphere(float size)
{
@@ -6016,21 +6289,6 @@ static void draw_empty_cone(float size)
gluDeleteQuadric(qobj);
}
-static void draw_textcurs(RegionView3D *rv3d, float textcurs[4][2])
-{
- cpack(0);
- ED_view3d_polygon_offset(rv3d, -1.0);
- set_inverted_drawing(1);
- glBegin(GL_QUADS);
- glVertex2fv(textcurs[0]);
- glVertex2fv(textcurs[1]);
- glVertex2fv(textcurs[2]);
- glVertex2fv(textcurs[3]);
- glEnd();
- set_inverted_drawing(0);
- ED_view3d_polygon_offset(rv3d, 0.0);
-}
-
static void drawspiral(const float cent[3], float rad, float tmat[4][4], int start)
{
float vec[3], vx[3], vy[3];
@@ -6904,27 +7162,24 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
Object *ob = base->object;
Curve *cu;
RegionView3D *rv3d = ar->regiondata;
- float vec1[3], vec2[3];
unsigned int col = 0;
unsigned char _ob_wire_col[4]; /* dont initialize this */
const unsigned char *ob_wire_col = NULL; /* dont initialize this, use NULL crashes as a way to find invalid use */
- int i, selstart, selend, empty_object = 0;
short dtx;
char dt;
- bool zbufoff = false, is_paint = false;
+ bool zbufoff = false, is_paint = false, empty_object = false;
const bool is_obact = (ob == OBACT);
const bool render_override = (v3d->flag2 & V3D_RENDER_OVERRIDE) != 0;
const bool is_picking = (G.f & G_PICKSEL) != 0;
bool particle_skip_object = false; /* Draw particles but not their emitter object. */
- /* only once set now, will be removed too, should become a global standard */
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
if (ob != scene->obedit) {
if (ob->restrictflag & OB_RESTRICT_VIEW) {
return;
}
- else if ((ob->restrictflag & OB_RESTRICT_RENDER) && render_override) {
+ else if (render_override && ((ob->restrictflag & OB_RESTRICT_RENDER) ||
+ (ob->transflag & OB_DUPLI)))
+ {
return;
}
}
@@ -6969,7 +7224,13 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
}
}
+
+ /* -------------------------------------------------------------------- */
/* no return after this point, otherwise leaks */
+
+ /* only once set now, will be removed too, should become a global standard */
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
view3d_cached_text_draw_begin();
/* draw motion paths (in view space) */
@@ -7069,125 +7330,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
case OB_FONT:
cu = ob->data;
if (cu->editfont) {
- EditFont *ef = cu->editfont;
-
- draw_textcurs(rv3d, ef->textcurs);
-
- if (cu->flag & CU_FAST) {
- cpack(0xFFFFFF);
- set_inverted_drawing(1);
- drawDispList(scene, v3d, rv3d, base, OB_WIRE, dflag, ob_wire_col);
- set_inverted_drawing(0);
- }
- else {
- drawDispList(scene, v3d, rv3d, base, dt, dflag, ob_wire_col);
- }
-
- if (cu->linewidth != 0.0f) {
- UI_ThemeColor(TH_WIRE_EDIT);
- copy_v3_v3(vec1, ob->orig);
- copy_v3_v3(vec2, ob->orig);
- vec1[0] += cu->linewidth;
- vec2[0] += cu->linewidth;
- vec1[1] += cu->linedist * cu->fsize;
- vec2[1] -= cu->lines * cu->linedist * cu->fsize;
- setlinestyle(3);
- glBegin(GL_LINE_STRIP);
- glVertex2fv(vec1);
- glVertex2fv(vec2);
- glEnd();
- setlinestyle(0);
- }
-
- setlinestyle(3);
- for (i = 0; i < cu->totbox; i++) {
- if (cu->tb[i].w != 0.0f) {
- UI_ThemeColor(i == (cu->actbox - 1) ? TH_ACTIVE : TH_WIRE);
- vec1[0] = cu->xof + cu->tb[i].x;
- vec1[1] = cu->yof + cu->tb[i].y + cu->fsize;
- vec1[2] = 0.001;
- glBegin(GL_LINE_STRIP);
- glVertex3fv(vec1);
- vec1[0] += cu->tb[i].w;
- glVertex3fv(vec1);
- vec1[1] -= cu->tb[i].h;
- glVertex3fv(vec1);
- vec1[0] -= cu->tb[i].w;
- glVertex3fv(vec1);
- vec1[1] += cu->tb[i].h;
- glVertex3fv(vec1);
- glEnd();
- }
- }
- setlinestyle(0);
-
-
- if (BKE_vfont_select_get(ob, &selstart, &selend) && ef->selboxes) {
- const int seltot = selend - selstart;
- float selboxw;
-
- cpack(0xffffff);
- set_inverted_drawing(1);
- for (i = 0; i <= seltot; i++) {
- EditFontSelBox *sb = &ef->selboxes[i];
- float tvec[3];
-
- if (i != seltot) {
- if (ef->selboxes[i + 1].y == sb->y)
- selboxw = ef->selboxes[i + 1].x - sb->x;
- else
- selboxw = sb->w;
- }
- else {
- selboxw = sb->w;
- }
-
- /* fill in xy below */
- tvec[2] = 0.001;
-
- glBegin(GL_QUADS);
-
- if (sb->rot == 0.0f) {
- copy_v2_fl2(tvec, sb->x, sb->y);
- glVertex3fv(tvec);
-
- copy_v2_fl2(tvec, sb->x + selboxw, sb->y);
- glVertex3fv(tvec);
-
- copy_v2_fl2(tvec, sb->x + selboxw, sb->y + sb->h);
- glVertex3fv(tvec);
-
- copy_v2_fl2(tvec, sb->x, sb->y + sb->h);
- glVertex3fv(tvec);
- }
- else {
- float mat[2][2];
-
- angle_to_mat2(mat, sb->rot);
-
- copy_v2_fl2(tvec, sb->x, sb->y);
- glVertex3fv(tvec);
-
- copy_v2_fl2(tvec, selboxw, 0.0f);
- mul_m2v2(mat, tvec);
- add_v2_v2(tvec, &sb->x);
- glVertex3fv(tvec);
-
- copy_v2_fl2(tvec, selboxw, sb->h);
- mul_m2v2(mat, tvec);
- add_v2_v2(tvec, &sb->x);
- glVertex3fv(tvec);
-
- copy_v2_fl2(tvec, 0.0f, sb->h);
- mul_m2v2(mat, tvec);
- add_v2_v2(tvec, &sb->x);
- glVertex3fv(tvec);
- }
-
- glEnd();
- }
- set_inverted_drawing(0);
- }
+ draw_editfont(scene, v3d, rv3d, base, dt, dflag, ob_wire_col);
}
else if (dt == OB_BOUNDBOX) {
if ((render_override && v3d->drawtype >= OB_WIRE) == 0) {
@@ -7741,23 +7884,22 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
static void bbs_obmode_mesh_verts__mapFunc(void *userData, int index, const float co[3],
const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
{
- bbsObmodeMeshVerts_userData *data = userData;
+ drawMVertOffset_userData *data = userData;
MVert *mv = &data->mvert[index];
- int offset = (intptr_t) data->offset;
if (!(mv->flag & ME_HIDE)) {
- WM_framebuffer_index_set(offset + index);
+ WM_framebuffer_index_set(data->offset + index);
bglVertex3fv(co);
}
}
static void bbs_obmode_mesh_verts(Object *ob, DerivedMesh *dm, int offset)
{
- bbsObmodeMeshVerts_userData data;
+ drawMVertOffset_userData data;
Mesh *me = ob->data;
MVert *mvert = me->mvert;
data.mvert = mvert;
- data.offset = (void *)(intptr_t) offset;
+ data.offset = offset;
glPointSize(UI_GetThemeValuef(TH_VERTEX_SIZE));
bglBegin(GL_POINTS);
dm->foreachMappedVert(dm, bbs_obmode_mesh_verts__mapFunc, &data, DM_FOREACH_NOP);
@@ -7768,34 +7910,31 @@ static void bbs_obmode_mesh_verts(Object *ob, DerivedMesh *dm, int offset)
static void bbs_mesh_verts__mapFunc(void *userData, int index, const float co[3],
const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
{
- void **ptrs = userData;
- int offset = (intptr_t) ptrs[0];
- BMVert *eve = BM_vert_at_index(ptrs[1], index);
+ drawBMOffset_userData *data = userData;
+ BMVert *eve = BM_vert_at_index(data->bm, index);
if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
- WM_framebuffer_index_set(offset + index);
+ WM_framebuffer_index_set(data->offset + index);
bglVertex3fv(co);
}
}
static void bbs_mesh_verts(BMEditMesh *em, DerivedMesh *dm, int offset)
{
- void *ptrs[2] = {(void *)(intptr_t) offset, em->bm};
-
+ drawBMOffset_userData data = {em->bm, offset};
glPointSize(UI_GetThemeValuef(TH_VERTEX_SIZE));
bglBegin(GL_POINTS);
- dm->foreachMappedVert(dm, bbs_mesh_verts__mapFunc, ptrs, DM_FOREACH_NOP);
+ dm->foreachMappedVert(dm, bbs_mesh_verts__mapFunc, &data, DM_FOREACH_NOP);
bglEnd();
glPointSize(1.0);
}
static DMDrawOption bbs_mesh_wire__setDrawOptions(void *userData, int index)
{
- void **ptrs = userData;
- int offset = (intptr_t) ptrs[0];
- BMEdge *eed = BM_edge_at_index(ptrs[1], index);
+ drawBMOffset_userData *data = userData;
+ BMEdge *eed = BM_edge_at_index(data->bm, index);
if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
- WM_framebuffer_index_set(offset + index);
+ WM_framebuffer_index_set(data->offset + index);
return DM_DRAW_OPTION_NORMAL;
}
else {
@@ -7804,18 +7943,31 @@ static DMDrawOption bbs_mesh_wire__setDrawOptions(void *userData, int index)
}
static void bbs_mesh_wire(BMEditMesh *em, DerivedMesh *dm, int offset)
{
- void *ptrs[2] = {(void *)(intptr_t) offset, em->bm};
- dm->drawMappedEdges(dm, bbs_mesh_wire__setDrawOptions, ptrs);
+ drawBMOffset_userData data = {em->bm, offset};
+ dm->drawMappedEdges(dm, bbs_mesh_wire__setDrawOptions, &data);
}
-static DMDrawOption bbs_mesh_solid__setSolidDrawOptions(void *userData, int index)
+/**
+ * dont set #WM_framebuffer_index_set. just use to mask other
+ */
+static DMDrawOption bbs_mesh_mask__setSolidDrawOptions(void *userData, int index)
{
- BMFace *efa = BM_face_at_index(((void **)userData)[0], index);
+ BMFace *efa = BM_face_at_index(userData, index);
if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
- if (((void **)userData)[1]) {
- WM_framebuffer_index_set(index + 1);
- }
+ return DM_DRAW_OPTION_NORMAL;
+ }
+ else {
+ return DM_DRAW_OPTION_SKIP;
+ }
+}
+
+static DMDrawOption bbs_mesh_solid__setSolidDrawOptions(void *userData, int index)
+{
+ BMFace *efa = BM_face_at_index(userData, index);
+
+ if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
+ WM_framebuffer_index_set(index + 1);
return DM_DRAW_OPTION_NORMAL;
}
else {
@@ -7825,7 +7977,7 @@ static DMDrawOption bbs_mesh_solid__setSolidDrawOptions(void *userData, int inde
static void bbs_mesh_solid__drawCenter(void *userData, int index, const float cent[3], const float UNUSED(no[3]))
{
- BMFace *efa = BM_face_at_index(((void **)userData)[0], index);
+ BMFace *efa = BM_face_at_index(userData, index);
if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
WM_framebuffer_index_set(index + 1);
@@ -7836,26 +7988,24 @@ static void bbs_mesh_solid__drawCenter(void *userData, int index, const float ce
/* two options, facecolors or black */
static void bbs_mesh_solid_EM(BMEditMesh *em, Scene *scene, View3D *v3d,
- Object *ob, DerivedMesh *dm, int facecol)
+ Object *ob, DerivedMesh *dm, bool use_faceselect)
{
- void *ptrs[2] = {em->bm, NULL}; //second one being null means to draw black
cpack(0);
- if (facecol) {
- ptrs[1] = (void *)(intptr_t) 1;
- dm->drawMappedFaces(dm, bbs_mesh_solid__setSolidDrawOptions, GPU_enable_material, NULL, ptrs, 0);
+ if (use_faceselect) {
+ dm->drawMappedFaces(dm, bbs_mesh_solid__setSolidDrawOptions, GPU_enable_material, NULL, em->bm, 0);
if (check_ob_drawface_dot(scene, v3d, ob->dt)) {
glPointSize(UI_GetThemeValuef(TH_FACEDOT_SIZE));
bglBegin(GL_POINTS);
- dm->foreachMappedFaceCenter(dm, bbs_mesh_solid__drawCenter, ptrs, DM_FOREACH_NOP);
+ dm->foreachMappedFaceCenter(dm, bbs_mesh_solid__drawCenter, em->bm, DM_FOREACH_NOP);
bglEnd();
}
}
else {
- dm->drawMappedFaces(dm, bbs_mesh_solid__setSolidDrawOptions, GPU_enable_material, NULL, ptrs, 0);
+ dm->drawMappedFaces(dm, bbs_mesh_mask__setSolidDrawOptions, GPU_enable_material, NULL, em->bm, 0);
}
}
@@ -7944,7 +8094,7 @@ void draw_object_backbufsel(Scene *scene, View3D *v3d, RegionView3D *rv3d, Objec
DM_update_materials(dm, ob);
- bbs_mesh_solid_EM(em, scene, v3d, ob, dm, ts->selectmode & SCE_SELECT_FACE);
+ bbs_mesh_solid_EM(em, scene, v3d, ob, dm, (ts->selectmode & SCE_SELECT_FACE) != 0);
if (ts->selectmode & SCE_SELECT_FACE)
bm_solidoffs = 1 + em->bm->totface;
else
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 1b61da21bb7..7a4634bf01a 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -760,7 +760,7 @@ static void view3d_main_area_listener(bScreen *sc, ScrArea *sa, ARegion *ar, wmN
break;
case ND_NLA:
case ND_KEYFRAME:
- if (wmn->action == NA_EDITED)
+ if (ELEM3(wmn->action, NA_EDITED, NA_ADDED, NA_REMOVED))
ED_region_tag_redraw(ar);
break;
case ND_ANIMCHAN:
@@ -1011,7 +1011,7 @@ static void view3d_buttons_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa
break;
case ND_NLA:
case ND_KEYFRAME:
- if (wmn->action == NA_EDITED)
+ if (ELEM3(wmn->action, NA_EDITED, NA_ADDED, NA_REMOVED))
ED_region_tag_redraw(ar);
break;
}
diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c
index a4d0b8007d4..cf589cdd946 100644
--- a/source/blender/editors/space_view3d/view3d_buttons.c
+++ b/source/blender/editors/space_view3d/view3d_buttons.c
@@ -1181,8 +1181,8 @@ void view3d_buttons_register(ARegionType *art)
strcpy(pt->idname, "VIEW3D_PT_gpencil");
strcpy(pt->label, N_("Grease Pencil")); /* XXX C panels are not available through RNA (bpy.types)! */
strcpy(pt->translation_context, BLF_I18NCONTEXT_DEFAULT_BPYRNA);
- pt->draw_header = gpencil_panel_standard_header;
- pt->draw = gpencil_panel_standard;
+ pt->draw_header = ED_gpencil_panel_standard_header;
+ pt->draw = ED_gpencil_panel_standard;
BLI_addtail(&art->paneltypes, pt);
pt = MEM_callocN(sizeof(PanelType), "spacetype view3d panel vgroup");
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index 217c5d71af4..8b2b8afbcad 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -37,6 +37,7 @@
#include "DNA_customdata_types.h"
#include "DNA_object_types.h"
#include "DNA_group_types.h"
+#include "DNA_mesh_types.h"
#include "DNA_key_types.h"
#include "DNA_lamp_types.h"
#include "DNA_scene_types.h"
@@ -2235,7 +2236,7 @@ float view3d_depth_near(ViewDepths *d)
return far == far_real ? FLT_MAX : far;
}
-void draw_depth_gpencil(Scene *scene, ARegion *ar, View3D *v3d)
+void ED_view3d_draw_depth_gpencil(Scene *scene, ARegion *ar, View3D *v3d)
{
short zbuf = v3d->zbuf;
RegionView3D *rv3d = ar->regiondata;
@@ -2255,14 +2256,14 @@ void draw_depth_gpencil(Scene *scene, ARegion *ar, View3D *v3d)
glEnable(GL_DEPTH_TEST);
if (v3d->flag2 & V3D_SHOW_GPENCIL) {
- draw_gpencil_view3d(scene, v3d, ar, true);
+ ED_gpencil_draw_view3d(scene, v3d, ar, true);
}
v3d->zbuf = zbuf;
}
-void draw_depth(Scene *scene, ARegion *ar, View3D *v3d, int (*func)(void *), bool alphaoverride)
+void ED_view3d_draw_depth(Scene *scene, ARegion *ar, View3D *v3d, bool alphaoverride)
{
RegionView3D *rv3d = ar->regiondata;
Base *base;
@@ -2303,11 +2304,9 @@ void draw_depth(Scene *scene, ARegion *ar, View3D *v3d, int (*func)(void *), boo
Scene *sce_iter;
for (SETLOOPER(scene->set, sce_iter, base)) {
if (v3d->lay & base->lay) {
- if (func == NULL || func(base)) {
- draw_object(scene, ar, v3d, base, 0);
- if (base->object->transflag & OB_DUPLI) {
- draw_dupli_objects_color(scene, ar, v3d, base, dflag_depth, TH_UNDEFINED);
- }
+ draw_object(scene, ar, v3d, base, 0);
+ if (base->object->transflag & OB_DUPLI) {
+ draw_dupli_objects_color(scene, ar, v3d, base, dflag_depth, TH_UNDEFINED);
}
}
}
@@ -2315,13 +2314,11 @@ void draw_depth(Scene *scene, ARegion *ar, View3D *v3d, int (*func)(void *), boo
for (base = scene->base.first; base; base = base->next) {
if (v3d->lay & base->lay) {
- if (func == NULL || func(base)) {
- /* dupli drawing */
- if (base->object->transflag & OB_DUPLI) {
- draw_dupli_objects_color(scene, ar, v3d, base, dflag_depth, TH_UNDEFINED);
- }
- draw_object(scene, ar, v3d, base, dflag_depth);
+ /* dupli drawing */
+ if (base->object->transflag & OB_DUPLI) {
+ draw_dupli_objects_color(scene, ar, v3d, base, dflag_depth, TH_UNDEFINED);
}
+ draw_object(scene, ar, v3d, base, dflag_depth);
}
}
@@ -2607,12 +2604,25 @@ static void view3d_draw_objects(
/* set zbuffer after we draw clipping region */
if (v3d->drawtype > OB_WIRE) {
v3d->zbuf = true;
- glEnable(GL_DEPTH_TEST);
}
else {
v3d->zbuf = false;
}
+ /* special case (depth for wire color) */
+ if (v3d->drawtype <= OB_WIRE) {
+ if (scene->obedit && scene->obedit->type == OB_MESH) {
+ Mesh *me = scene->obedit->data;
+ if (me->drawflag & ME_DRAWEIGHT) {
+ v3d->zbuf = true;
+ }
+ }
+ }
+
+ if (v3d->zbuf) {
+ glEnable(GL_DEPTH_TEST);
+ }
+
if (!draw_offscreen) {
/* needs to be done always, gridview is adjusted in drawgrid() now, but only for ortho views. */
rv3d->gridview = v3d->grid;
@@ -2714,7 +2724,7 @@ static void view3d_draw_objects(
if (v3d->flag2 & V3D_SHOW_GPENCIL) {
/* must be before xray draw which clears the depth buffer */
if (v3d->zbuf) glDisable(GL_DEPTH_TEST);
- draw_gpencil_view3d(scene, v3d, ar, true);
+ ED_gpencil_draw_view3d(scene, v3d, ar, true);
if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
}
@@ -2834,7 +2844,7 @@ void ED_view3d_draw_offscreen(Scene *scene, View3D *v3d, ARegion *ar, int winx,
if (v3d->flag2 & V3D_SHOW_GPENCIL) {
/* draw grease-pencil stuff - needed to get paint-buffer shown too (since it's 2D) */
- draw_gpencil_view3d(scene, v3d, ar, false);
+ ED_gpencil_draw_view3d(scene, v3d, ar, false);
}
/* freeing the images again here could be done after the operator runs, leaving for now */
@@ -2975,10 +2985,11 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, Object *camera, int w
}
-/* NOTE: the info that this uses is updated in ED_refresh_viewport_fps(),
- * which currently gets called during SCREEN_OT_animation_step.
+/**
+ * \note The info that this uses is updated in #ED_refresh_viewport_fps,
+ * which currently gets called during #SCREEN_OT_animation_step.
*/
-void ED_scene_draw_fps(Scene *scene, rcti *rect)
+void ED_scene_draw_fps(Scene *scene, const rcti *rect)
{
ScreenFrameRateInfo *fpsi = scene->fps_info;
float fps;
@@ -3449,7 +3460,7 @@ static void view3d_main_area_draw_info(const bContext *C, Scene *scene,
if (v3d->flag2 & V3D_SHOW_GPENCIL) {
/* draw grease-pencil stuff - needed to get paint-buffer shown too (since it's 2D) */
- draw_gpencil_view3d(scene, v3d, ar, false);
+ ED_gpencil_draw_view3d(scene, v3d, ar, false);
}
if ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) {
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 6099ef49149..26ede27bb08 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -558,7 +558,7 @@ static bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
*/
if (ob->mode & OB_MODE_SCULPT) {
float stroke[3];
- ED_sculpt_get_average_stroke(ob, stroke);
+ ED_sculpt_stroke_get_average(ob, stroke);
copy_v3_v3(lastofs, stroke);
}
else {
@@ -3276,7 +3276,7 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
/* Get Z Depths, needed for perspective, nice for ortho */
bgl_get_mats(&mats);
- draw_depth(scene, ar, v3d, NULL, true);
+ ED_view3d_draw_depth(scene, ar, v3d, true);
{
/* avoid allocating the whole depth buffer */
@@ -4511,7 +4511,7 @@ bool ED_view3d_autodist(Scene *scene, ARegion *ar, View3D *v3d,
/* Get Z Depths, needed for perspective, nice for ortho */
bgl_get_mats(&mats);
- draw_depth(scene, ar, v3d, NULL, alphaoverride);
+ ED_view3d_draw_depth(scene, ar, v3d, alphaoverride);
depth_close = view_autodist_depth_margin(ar, mval, 4);
@@ -4543,10 +4543,10 @@ void ED_view3d_autodist_init(Scene *scene, ARegion *ar, View3D *v3d, int mode)
/* Get Z Depths, needed for perspective, nice for ortho */
switch (mode) {
case 0:
- draw_depth(scene, ar, v3d, NULL, true);
+ ED_view3d_draw_depth(scene, ar, v3d, true);
break;
case 1:
- draw_depth_gpencil(scene, ar, v3d);
+ ED_view3d_draw_depth_gpencil(scene, ar, v3d);
break;
}
}
diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h
index 2c6fc1cfe02..169bc494e1b 100644
--- a/source/blender/editors/space_view3d/view3d_intern.h
+++ b/source/blender/editors/space_view3d/view3d_intern.h
@@ -177,8 +177,8 @@ void draw_mesh_paint(View3D *v3d, RegionView3D *rv3d,
/* view3d_draw.c */
void view3d_main_area_draw(const struct bContext *C, struct ARegion *ar);
-void draw_depth(Scene *scene, struct ARegion *ar, View3D *v3d, int (*func)(void *), bool alphaoverride);
-void draw_depth_gpencil(Scene *scene, ARegion *ar, View3D *v3d);
+void ED_view3d_draw_depth(Scene *scene, struct ARegion *ar, View3D *v3d, bool alphaoverride);
+void ED_view3d_draw_depth_gpencil(Scene *scene, ARegion *ar, View3D *v3d);
void ED_view3d_after_add(ListBase *lb, Base *base, const short dflag);
void circf(float x, float y, float rad);
@@ -218,7 +218,7 @@ void ED_view3d_smooth_view(
const float *ofs, const float *quat, const float *dist, const float *lens,
const int smooth_viewtx);
-void view3d_winmatrix_set(ARegion *ar, View3D *v3d, rctf *rect);
+void view3d_winmatrix_set(ARegion *ar, View3D *v3d, const rctf *rect);
void view3d_viewmatrix_set(Scene *scene, View3D *v3d, RegionView3D *rv3d);
void fly_modal_keymap(struct wmKeyConfig *keyconf);
diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c
index a8128ba7ae8..612bdfa27b9 100644
--- a/source/blender/editors/space_view3d/view3d_ops.c
+++ b/source/blender/editors/space_view3d/view3d_ops.c
@@ -77,7 +77,7 @@ static int view3d_copybuffer_exec(bContext *C, wmOperator *op)
}
CTX_DATA_END;
- BLI_make_file_string("/", str, BLI_temporary_dir(), "copybuffer.blend");
+ BLI_make_file_string("/", str, BLI_temp_dir_session(), "copybuffer.blend");
BKE_copybuffer_save(str, op->reports);
BKE_report(op->reports, RPT_INFO, "Copied selected objects to buffer");
@@ -102,7 +102,7 @@ static int view3d_pastebuffer_exec(bContext *C, wmOperator *op)
{
char str[FILE_MAX];
- BLI_make_file_string("/", str, BLI_temporary_dir(), "copybuffer.blend");
+ BLI_make_file_string("/", str, BLI_temp_dir_session(), "copybuffer.blend");
if (BKE_copybuffer_paste(C, str, op->reports)) {
WM_event_add_notifier(C, NC_WINDOW, NULL);
diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c
index 00ce51a2c0d..9b14df402ec 100644
--- a/source/blender/editors/space_view3d/view3d_project.c
+++ b/source/blender/editors/space_view3d/view3d_project.c
@@ -320,6 +320,7 @@ static void view3d_win_to_ray_segment(const ARegion *ar, View3D *v3d, const floa
end_offset = v3d->far;
}
else {
+ const float ortho_extent = 1000.0f;
float vec[4];
vec[0] = 2.0f * mval[0] / ar->winx - 1;
vec[1] = 2.0f * mval[1] / ar->winy - 1;
@@ -329,8 +330,8 @@ static void view3d_win_to_ray_segment(const ARegion *ar, View3D *v3d, const floa
mul_m4_v4(rv3d->persinv, vec);
copy_v3_v3(r_ray_co, vec);
- start_offset = -1000.0f;
- end_offset = 1000.0f;
+ start_offset = (rv3d->persp == RV3D_CAMOB) ? 0.0f : -ortho_extent;
+ end_offset = ortho_extent;
}
if (r_ray_start) {
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 063f828c3b0..32f063d6154 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -2128,7 +2128,7 @@ static int view3d_borderselect_exec(bContext *C, wmOperator *op)
}
else { /* no editmode, unified for bones and objects */
if (vc.obact && vc.obact->mode & OB_MODE_SCULPT) {
- ret = do_sculpt_mask_box_select(C, &vc, &rect, select, extend);
+ ret = ED_sculpt_mask_box_select(C, &vc, &rect, select, extend);
}
else if (vc.obact && BKE_paint_select_face_test(vc.obact)) {
ret = do_paintface_box_select(&vc, &rect, select, extend);
@@ -2841,6 +2841,6 @@ void VIEW3D_OT_select_circle(wmOperatorType *ot)
RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
- RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "radius", 1, 1, INT_MAX, "Radius", "", 1, INT_MAX);
RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
}
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index ef99e8d873c..716f4b10fae 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -784,11 +784,11 @@ void ED_view3d_polygon_offset(const RegionView3D *rv3d, const float dist)
/**
* \param rect optional for picking (can be NULL).
*/
-void view3d_winmatrix_set(ARegion *ar, View3D *v3d, rctf *rect)
+void view3d_winmatrix_set(ARegion *ar, View3D *v3d, const rctf *rect)
{
RegionView3D *rv3d = ar->regiondata;
rctf viewplane;
- float clipsta, clipend, x1, y1, x2, y2;
+ float clipsta, clipend;
bool is_ortho;
is_ortho = ED_view3d_viewplane_get(v3d, rv3d, ar->winx, ar->winy, &viewplane, &clipsta, &clipend, NULL);
@@ -800,28 +800,20 @@ void view3d_winmatrix_set(ARegion *ar, View3D *v3d, rctf *rect)
clipsta, clipend);
#endif
- x1 = viewplane.xmin;
- y1 = viewplane.ymin;
- x2 = viewplane.xmax;
- y2 = viewplane.ymax;
-
if (rect) { /* picking */
- rect->xmin /= (float)ar->winx;
- rect->xmin = x1 + rect->xmin * (x2 - x1);
- rect->ymin /= (float)ar->winy;
- rect->ymin = y1 + rect->ymin * (y2 - y1);
- rect->xmax /= (float)ar->winx;
- rect->xmax = x1 + rect->xmax * (x2 - x1);
- rect->ymax /= (float)ar->winy;
- rect->ymax = y1 + rect->ymax * (y2 - y1);
-
- if (is_ortho) wmOrtho(rect->xmin, rect->xmax, rect->ymin, rect->ymax, -clipend, clipend);
- else wmFrustum(rect->xmin, rect->xmax, rect->ymin, rect->ymax, clipsta, clipend);
+ rctf r;
+ r.xmin = viewplane.xmin + (BLI_rctf_size_x(&viewplane) * (rect->xmin / (float)ar->winx));
+ r.ymin = viewplane.ymin + (BLI_rctf_size_y(&viewplane) * (rect->ymin / (float)ar->winy));
+ r.xmax = viewplane.xmin + (BLI_rctf_size_x(&viewplane) * (rect->xmax / (float)ar->winx));
+ r.ymax = viewplane.ymin + (BLI_rctf_size_y(&viewplane) * (rect->ymax / (float)ar->winy));
+ viewplane = r;
+ }
+ if (is_ortho) {
+ wmOrtho(viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, clipsta, clipend);
}
else {
- if (is_ortho) wmOrtho(x1, x2, y1, y2, clipsta, clipend);
- else wmFrustum(x1, x2, y1, y2, clipsta, clipend);
+ wmFrustum(viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, clipsta, clipend);
}
/* update matrix in 3d view region */
@@ -970,7 +962,7 @@ void view3d_viewmatrix_set(Scene *scene, View3D *v3d, RegionView3D *rv3d)
*
* \note (vc->obedit == NULL) can be set to explicitly skip edit-object selection.
*/
-short view3d_opengl_select(ViewContext *vc, unsigned int *buffer, unsigned int bufsize, rcti *input)
+short view3d_opengl_select(ViewContext *vc, unsigned int *buffer, unsigned int bufsize, const rcti *input)
{
Scene *scene = vc->scene;
View3D *v3d = vc->v3d;
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index 899f1191e33..33d52ec1a00 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -1718,7 +1718,7 @@ static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata)
{
float dx = t->mval[0] - cent[0], dy = t->mval[1] - cent[1];
float angle = atan2f(dy, dx);
- float dist = sqrtf(dx * dx + dy * dy);
+ float dist = hypotf(dx, dy);
float delta_angle = min_ff(15.0f / dist, (float)M_PI / 4.0f);
float spacing_angle = min_ff(5.0f / dist, (float)M_PI / 12.0f);
UI_ThemeColor(TH_VIEW_OVERLAY);
@@ -7342,13 +7342,17 @@ static void headerTimeTranslate(TransInfo *t, char str[MAX_INFO_LEN])
/* second step */
val = floorf((double)val / secf + 0.5);
}
- else {
- /* nearest frame/second/marker */
+ else if (autosnap == SACTSNAP_SECOND) {
+ /* nearest second */
val = (float)((double)val / secf);
}
if (autosnap == SACTSNAP_FRAME)
BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%d.00 (%.4f)", (int)val, val);
+ else if (autosnap == SACTSNAP_SECOND)
+ BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%d.00 sec (%.4f)", (int)val, val);
+ else if (autosnap == SACTSNAP_TSTEP)
+ BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f sec", val);
else
BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", val);
}
diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c
index 96e75471c15..ab0d97791be 100644
--- a/source/blender/editors/transform/transform_conversions.c
+++ b/source/blender/editors/transform/transform_conversions.c
@@ -1899,37 +1899,45 @@ static void editmesh_set_connectivity_distance(BMesh *bm, float mtx[3][3], float
memcpy(dists_prev, dists, sizeof(float) * bm->totvert);
while ((v = BLI_LINKSTACK_POP(queue))) {
- BMIter iter;
- BMEdge *e;
- BMLoop *l;
+ /* quick checks */
+ bool has_edges = (v->e != NULL);
+ bool has_faces = false;
/* connected edge-verts */
- BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
- if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == 0) {
- BMVert *v_other = BM_edge_other_vert(e, v);
- if (bmesh_test_dist_add(v, v_other, dists, dists_prev, mtx)) {
- if (BM_elem_flag_test(v_other, BM_ELEM_TAG) == 0) {
- BM_elem_flag_enable(v_other, BM_ELEM_TAG);
- BLI_LINKSTACK_PUSH(queue_next, v_other);
+ if (has_edges) {
+ BMIter iter;
+ BMEdge *e;
+
+ BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
+ has_faces |= (BM_edge_is_wire(e) == false);
+
+ if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == 0) {
+ BMVert *v_other = BM_edge_other_vert(e, v);
+ if (bmesh_test_dist_add(v, v_other, dists, dists_prev, mtx)) {
+ if (BM_elem_flag_test(v_other, BM_ELEM_TAG) == 0) {
+ BM_elem_flag_enable(v_other, BM_ELEM_TAG);
+ BLI_LINKSTACK_PUSH(queue_next, v_other);
+ }
}
}
}
}
- /* connected face-verts (excluding adjacent verts) */
- BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
- if ((BM_elem_flag_test(l->f, BM_ELEM_HIDDEN) == 0) && (l->f->len > 3)) {
- BMLoop *l_end = l->prev;
- l = l->next->next;
- do {
- BMVert *v_other = l->v;
+ /* imaginary edge diagonally across quad */
+ if (has_faces) {
+ BMIter iter;
+ BMLoop *l;
+
+ BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
+ if ((BM_elem_flag_test(l->f, BM_ELEM_HIDDEN) == 0) && (l->f->len == 4)) {
+ BMVert *v_other = l->next->next->v;
if (bmesh_test_dist_add(v, v_other, dists, dists_prev, mtx)) {
if (BM_elem_flag_test(v_other, BM_ELEM_TAG) == 0) {
BM_elem_flag_enable(v_other, BM_ELEM_TAG);
BLI_LINKSTACK_PUSH(queue_next, v_other);
}
}
- } while ((l = l->next) != l_end);
+ }
}
}
}
@@ -2623,7 +2631,7 @@ static void createTransUVs(bContext *C, TransInfo *t)
if (propconnected) {
UvElement *element = BM_uv_element_get(elementmap, efa, l);
- BLI_BITMAP_SET(island_enabled, element->island);
+ BLI_BITMAP_ENABLE(island_enabled, element->island);
}
}
@@ -2664,7 +2672,7 @@ static void createTransUVs(bContext *C, TransInfo *t)
if (propconnected) {
UvElement *element = BM_uv_element_get(elementmap, efa, l);
- if (!BLI_BITMAP_GET(island_enabled, element->island)) {
+ if (!BLI_BITMAP_TEST(island_enabled, element->island)) {
count_rejected++;
continue;
}
@@ -4101,8 +4109,8 @@ void flushTransGraphData(TransInfo *t)
a++, td++, td2d++, tdg++)
{
AnimData *adt = (AnimData *)td->extra; /* pointers to relevant AnimData blocks are stored in the td->extra pointers */
- float unit_scale = tdg->unit_scale;
-
+ float inv_unit_scale = 1.0f / tdg->unit_scale;
+
/* handle snapping for time values
* - we should still be in NLA-mapping timespace
* - only apply to keyframes (but never to handles)
@@ -4159,16 +4167,16 @@ void flushTransGraphData(TransInfo *t)
if (td->flag & TD_INTVALUES)
td2d->loc2d[1] = floorf(td2d->loc[1] + 0.5f);
else
- td2d->loc2d[1] = td2d->loc[1] / unit_scale;
+ td2d->loc2d[1] = td2d->loc[1] * inv_unit_scale;
if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) {
td2d->h1[0] = td2d->ih1[0] + td->loc[0] - td->iloc[0];
- td2d->h1[1] = td2d->ih1[1] + td->loc[1] - td->iloc[1];
+ td2d->h1[1] = td2d->ih1[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale;
}
if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) {
td2d->h2[0] = td2d->ih2[0] + td->loc[0] - td->iloc[0];
- td2d->h2[1] = td2d->ih2[1] + td->loc[1] - td->iloc[1];
+ td2d->h2[1] = td2d->ih2[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale;
}
}
}
diff --git a/source/blender/editors/transform/transform_input.c b/source/blender/editors/transform/transform_input.c
index 6546a05aedd..61b2deabe12 100644
--- a/source/blender/editors/transform/transform_input.c
+++ b/source/blender/editors/transform/transform_input.c
@@ -64,18 +64,18 @@ static void InputSpring(TransInfo *UNUSED(t), MouseInput *mi, const int mval[2],
/* calculate ratio for shiftkey pos, and for total, and blend these for precision */
dx = (float)(mi->center[0] - mi->precision_mval[0]);
dy = (float)(mi->center[1] - mi->precision_mval[1]);
- ratio = sqrtf(dx * dx + dy * dy);
+ ratio = hypotf(dx, dy);
dx = (float)(mi->center[0] - mval[0]);
dy = (float)(mi->center[1] - mval[1]);
- precise_ratio = sqrtf(dx * dx + dy * dy);
+ precise_ratio = hypotf(dx, dy);
ratio = (ratio + (precise_ratio - ratio) / 10.0f) / mi->factor;
}
else {
dx = (float)(mi->center[0] - mval[0]);
dy = (float)(mi->center[1] - mval[1]);
- ratio = sqrtf(dx * dx + dy * dy) / mi->factor;
+ ratio = hypotf(dx, dy) / mi->factor;
}
output[0] = ratio;
@@ -195,7 +195,7 @@ static void InputCustomRatioFlip(TransInfo *UNUSED(t), MouseInput *mi, const int
dx = data[2] - data[0];
dy = data[3] - data[1];
- length = sqrt(dx * dx + dy * dy);
+ length = hypot(dx, dy);
if (mi->precision) {
/* deal with Shift key by adding motion / 10 to motion before shift press */
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index 9afc12a5270..451837fd311 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -1489,13 +1489,12 @@ static bool snapCurve(short snap_mode, ARegion *ar, Object *ob, Curve *cu, float
static bool snapDerivedMesh(short snap_mode, ARegion *ar, Object *ob, DerivedMesh *dm, BMEditMesh *em, float obmat[4][4],
const float ray_start[3], const float ray_normal[3], const float ray_origin[3],
- const float mval[2], float r_loc[3], float r_no[3], float *r_dist_px, float *r_depth)
+ const float mval[2], float r_loc[3], float r_no[3], float *r_dist_px, float *r_depth, bool do_bb)
{
bool retval = false;
int totvert = dm->getNumVerts(dm);
if (totvert > 0) {
- BoundBox *bb;
float imat[4][4];
float timat[3][3]; /* transpose inverse matrix for normals */
float ray_start_local[3], ray_normal_local[3], local_scale, len_diff = TRANSFORM_DIST_MAX_RAY;
@@ -1513,9 +1512,11 @@ static bool snapDerivedMesh(short snap_mode, ARegion *ar, Object *ob, DerivedMes
/* local scale in normal direction */
local_scale = normalize_v3(ray_normal_local);
- bb = BKE_object_boundbox_get(ob);
- if (!BKE_boundbox_ray_hit_check(bb, ray_start_local, ray_normal_local, &len_diff)) {
- return retval;
+ if (do_bb) {
+ BoundBox *bb = BKE_object_boundbox_get(ob);
+ if (!BKE_boundbox_ray_hit_check(bb, ray_start_local, ray_normal_local, &len_diff)) {
+ return retval;
+ }
}
switch (snap_mode) {
@@ -1813,17 +1814,19 @@ static bool snapObject(Scene *scene, short snap_mode, ARegion *ar, Object *ob, f
if (ob->type == OB_MESH) {
BMEditMesh *em;
DerivedMesh *dm;
+ bool do_bb = true;
if (use_obedit) {
em = BKE_editmesh_from_object(ob);
dm = editbmesh_get_derived_cage(scene, ob, em, CD_MASK_BAREMESH);
+ do_bb = false;
}
else {
em = NULL;
dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
}
- retval = snapDerivedMesh(snap_mode, ar, ob, dm, em, obmat, ray_start, ray_normal, ray_origin, mval, r_loc, r_no, r_dist_px, r_depth);
+ retval = snapDerivedMesh(snap_mode, ar, ob, dm, em, obmat, ray_start, ray_normal, ray_origin, mval, r_loc, r_no, r_dist_px, r_depth, do_bb);
dm->release(dm);
}
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index f52a406d2b1..f4189a18da4 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -69,6 +69,7 @@ set(SRC
../include/ED_node.h
../include/ED_numinput.h
../include/ED_object.h
+ ../include/ED_paint.h
../include/ED_particle.h
../include/ED_physics.h
../include/ED_render.h
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index 2580836cad9..47fbfbe3eba 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -41,7 +41,9 @@
#include "DNA_scene_types.h"
#include "DNA_packedFile_types.h"
-#include "BLI_blenlib.h"
+#include "BLI_utildefines.h"
+#include "BLI_string.h"
+#include "BLI_path_util.h"
#include "BIF_gl.h"
#include "BIF_glutil.h"
@@ -59,7 +61,7 @@
#include "ED_image.h"
#include "ED_mesh.h"
#include "ED_object.h"
-#include "ED_sculpt.h"
+#include "ED_paint.h"
#include "ED_space_api.h"
#include "ED_util.h"
@@ -82,14 +84,20 @@ void ED_editors_init(bContext *C)
Object *ob, *obact = (sce && sce->basact) ? sce->basact->object : NULL;
ID *data;
+ /* This is called during initialization, so we don't want to store any reports */
+ ReportList *reports = CTX_wm_reports(C);
+ int reports_flag_prev = reports->flag &= ~RPT_STORE;
+
+ SWAP(int, reports->flag, reports_flag_prev);
+
/* toggle on modes for objects that were saved with these enabled. for
* e.g. linked objects we have to ensure that they are actually the
* active object in this scene. */
for (ob = bmain->object.first; ob; ob = ob->id.next) {
int mode = ob->mode;
- if (mode && (mode != OB_MODE_POSE)) {
- ob->mode = 0;
+ if (!ELEM(mode, OB_MODE_OBJECT, OB_MODE_POSE)) {
+ ob->mode = OB_MODE_OBJECT;
data = ob->data;
if (ob == obact && !ob->id.lib && !(data && data->lib))
@@ -101,6 +109,8 @@ void ED_editors_init(bContext *C)
if (sce) {
ED_space_image_paint_update(wm, sce->toolsettings);
}
+
+ SWAP(int, reports->flag, reports_flag_prev);
}
/* frees all editmode stuff */
@@ -145,25 +155,27 @@ void ED_editors_exit(bContext *C)
* rendering, copying, etc. */
void ED_editors_flush_edits(const bContext *C, bool for_render)
{
- Object *obact = CTX_data_active_object(C);
+ Object *ob;
Object *obedit = CTX_data_edit_object(C);
-
+ Main *bmain = CTX_data_main(C);
/* get editmode results */
if (obedit)
ED_object_editmode_load(obedit);
- if (obact && (obact->mode & OB_MODE_SCULPT)) {
- /* flush multires changes (for sculpt) */
- multires_force_update(obact);
+ for (ob = bmain->object.first; ob; ob = ob->id.next) {
+ if (ob && (ob->mode & OB_MODE_SCULPT)) {
+ /* flush multires changes (for sculpt) */
+ multires_force_update(ob);
- if (for_render) {
- /* flush changes from dynamic topology sculpt */
- BKE_sculptsession_bm_to_me_for_render(obact);
- }
- else {
- /* Set reorder=false so that saving the file doesn't reorder
+ if (for_render) {
+ /* flush changes from dynamic topology sculpt */
+ BKE_sculptsession_bm_to_me_for_render(ob);
+ }
+ else {
+ /* Set reorder=false so that saving the file doesn't reorder
* the BMesh's elements */
- BKE_sculptsession_bm_to_me(obact, false);
+ BKE_sculptsession_bm_to_me(ob, false);
+ }
}
}
}
diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c
index ee391af185d..26b9d8b5d71 100644
--- a/source/blender/editors/util/numinput.c
+++ b/source/blender/editors/util/numinput.c
@@ -256,7 +256,6 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
short idx = n->idx, idx_max = n->idx_max;
short dir = STRCUR_DIR_NEXT, mode = STRCUR_JUMP_NONE;
int cur;
- double val;
switch (event->type) {
case EVT_MODAL_MAP:
@@ -467,6 +466,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
/* At this point, our value has changed, try to interpret it with python (if str is not empty!). */
if (n->str[0]) {
#ifdef WITH_PYTHON
+ double val;
char str_unit_convert[NUM_STR_REP_LEN * 6]; /* Should be more than enough! */
const char *default_unit = NULL;
@@ -489,6 +489,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
}
#else /* Very unlikely, but does not harm... */
n->val[idx] = (float)atof(n->str);
+ (void)C;
#endif /* WITH_PYTHON */
if (n->val_flag[idx] & NUM_NEGATE) {
diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c
index ee02e0c396a..9461010f49f 100644
--- a/source/blender/editors/util/undo.c
+++ b/source/blender/editors/util/undo.c
@@ -58,7 +58,7 @@
#include "ED_object.h"
#include "ED_render.h"
#include "ED_screen.h"
-#include "ED_sculpt.h"
+#include "ED_paint.h"
#include "ED_util.h"
#include "ED_text.h"
@@ -385,7 +385,13 @@ int ED_undo_operator_repeat(bContext *C, struct wmOperator *op)
ED_undo_pop_op(C, op);
if (op->type->check) {
- op->type->check(C, op); /* ignore return value since its running again anyway */
+ if (op->type->check(C, op)) {
+ /* check for popup and re-layout buttons */
+ ARegion *ar_menu = CTX_wm_menu(C);
+ if (ar_menu) {
+ ED_region_tag_refresh_ui(ar_menu);
+ }
+ }
}
retval = WM_operator_repeat(C, op);
diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c
index 7c2f71c0153..a1cc23f735b 100644
--- a/source/blender/editors/uvedit/uvedit_draw.c
+++ b/source/blender/editors/uvedit/uvedit_draw.c
@@ -418,7 +418,7 @@ static void draw_uvs_other_mesh_new_shading(Object *ob, const Image *curimage)
Image *image;
ED_object_get_active_image(ob, a + 1, &image, NULL, NULL);
if (image == curimage) {
- BLI_BITMAP_SET(mat_test_array, a);
+ BLI_BITMAP_ENABLE(mat_test_array, a);
ok = true;
}
}
@@ -430,7 +430,7 @@ static void draw_uvs_other_mesh_new_shading(Object *ob, const Image *curimage)
for (a = me->totpoly; a != 0; a--, mpoly++) {
const int mat_nr = mpoly->mat_nr;
if ((mat_nr >= ob->totcol) ||
- (BLI_BITMAP_GET(mat_test_array, mat_nr)) == 0)
+ (BLI_BITMAP_TEST(mat_test_array, mat_nr)) == 0)
{
continue;
}
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index 5169cc73052..6cf34d9f93f 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -3026,7 +3026,7 @@ static void UV_OT_circle_select(wmOperatorType *ot)
/* properties */
RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
- RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "radius", 1, 1, INT_MAX, "Radius", "", 1, INT_MAX);
RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
}
diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp
index 72baea156a0..b504d2c8c40 100644
--- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp
+++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp
@@ -28,6 +28,8 @@
#include "BKE_global.h"
+#include <sstream>
+
namespace Freestyle {
BlenderFileLoader::BlenderFileLoader(Render *re, SceneRenderLayer *srl)
@@ -38,6 +40,7 @@ BlenderFileLoader::BlenderFileLoader(Render *re, SceneRenderLayer *srl)
_numFacesRead = 0;
_minEdgeSize = DBL_MAX;
_smooth = (srl->freestyleConfig.flags & FREESTYLE_FACE_SMOOTHNESS_FLAG) != 0;
+ _pRenderMonitor = NULL;
}
BlenderFileLoader::~BlenderFileLoader()
@@ -86,9 +89,21 @@ NodeGroup *BlenderFileLoader::Load()
#endif
int id = 0;
+ unsigned cnt = 1;
+ unsigned cntStep = (unsigned)ceil(0.01f * _re->totinstance);
for (obi = (ObjectInstanceRen *)_re->instancetable.first; obi; obi = obi->next) {
- if (_pRenderMonitor && _pRenderMonitor->testBreak())
- break;
+ if (_pRenderMonitor) {
+ if (_pRenderMonitor->testBreak())
+ break;
+ if (cnt % cntStep == 0) {
+ stringstream ss;
+ ss << "Freestyle: Mesh loading " << (100 * cnt / _re->totinstance) << "%";
+ _pRenderMonitor->setInfo(ss.str());
+ _pRenderMonitor->progress((float)cnt / _re->totinstance);
+ }
+ cnt++;
+ }
+
if (!(obi->lay & _srl->lay))
continue;
char *name = obi->ob->id.name;
diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
index 49c3fdce251..9474ce7994d 100644
--- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
+++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
@@ -84,8 +84,6 @@ int freestyle_viewport[4];
// current scene
Scene *freestyle_scene;
-static string default_module_path;
-
static void load_post_callback(struct Main *main, struct ID *id, void *arg)
{
lineset_copied = false;
@@ -115,9 +113,6 @@ void FRS_initialize()
freestyle_scene = NULL;
lineset_copied = false;
- default_module_path = pathconfig->getProjectDir() + Config::DIR_SEP + "style_modules" +
- Config::DIR_SEP + "contour.py";
-
BLI_callback_add(&load_post_callback_funcstore, BLI_CB_EVT_LOAD_POST);
freestyle_is_initialized = 1;
@@ -611,25 +606,25 @@ Render *FRS_do_stroke_rendering(Render *re, SceneRenderLayer *srl, int render)
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "Break" << endl;
}
- return NULL;
}
-
- // render and composite Freestyle result
- if (controller->_ViewMap) {
- // render strokes
- re->i.infostr = "Freestyle: Stroke rendering";
- re->stats_draw(re->sdh, &re->i);
- re->i.infostr = NULL;
- freestyle_scene = re->scene;
- controller->DrawStrokes();
- freestyle_render = controller->RenderStrokes(re, true);
- controller->CloseFile();
- freestyle_scene = NULL;
-
- // composite result
- FRS_composite_result(re, srl, freestyle_render);
- RE_FreeRenderResult(freestyle_render->result);
- freestyle_render->result = NULL;
+ else {
+ // render and composite Freestyle result
+ if (controller->_ViewMap) {
+ // render strokes
+ re->i.infostr = "Freestyle: Stroke rendering";
+ re->stats_draw(re->sdh, &re->i);
+ re->i.infostr = NULL;
+ freestyle_scene = re->scene;
+ controller->DrawStrokes();
+ freestyle_render = controller->RenderStrokes(re, true);
+ controller->CloseFile();
+ freestyle_scene = NULL;
+
+ // composite result
+ FRS_composite_result(re, srl, freestyle_render);
+ RE_FreeRenderResult(freestyle_render->result);
+ freestyle_render->result = NULL;
+ }
}
// Free temp main (currently only text blocks are stored there)
diff --git a/source/blender/freestyle/intern/python/BPy_Convert.cpp b/source/blender/freestyle/intern/python/BPy_Convert.cpp
index 746d5e75f4c..78c3599b7cd 100644
--- a/source/blender/freestyle/intern/python/BPy_Convert.cpp
+++ b/source/blender/freestyle/intern/python/BPy_Convert.cpp
@@ -201,7 +201,7 @@ PyObject *BPy_Interface0D_from_Interface0D(Interface0D& if0D)
{
PyObject *py_if0D = Interface0D_Type.tp_new(&Interface0D_Type, 0, 0);
((BPy_Interface0D *)py_if0D)->if0D = &if0D;
- ((BPy_Interface0D *)py_if0D)->borrowed = 1;
+ ((BPy_Interface0D *)py_if0D)->borrowed = true;
return py_if0D;
}
@@ -209,7 +209,7 @@ PyObject *BPy_Interface1D_from_Interface1D(Interface1D& if1D)
{
PyObject *py_if1D = Interface1D_Type.tp_new(&Interface1D_Type, 0, 0);
((BPy_Interface1D *)py_if1D)->if1D = &if1D;
- ((BPy_Interface1D *)py_if1D)->borrowed = 1;
+ ((BPy_Interface1D *)py_if1D)->borrowed = true;
return py_if1D;
}
@@ -218,7 +218,7 @@ PyObject *BPy_SVertex_from_SVertex(SVertex& sv)
PyObject *py_sv = SVertex_Type.tp_new(&SVertex_Type, 0, 0);
((BPy_SVertex *)py_sv)->sv = &sv;
((BPy_SVertex *)py_sv)->py_if0D.if0D = ((BPy_SVertex *)py_sv)->sv;
- ((BPy_SVertex *)py_sv)->py_if0D.borrowed = 1;
+ ((BPy_SVertex *)py_sv)->py_if0D.borrowed = true;
return py_sv;
}
@@ -228,7 +228,7 @@ PyObject *BPy_FEdgeSharp_from_FEdgeSharp(FEdgeSharp& fes)
((BPy_FEdgeSharp *)py_fe)->fes = &fes;
((BPy_FEdgeSharp *)py_fe)->py_fe.fe = ((BPy_FEdgeSharp *)py_fe)->fes;
((BPy_FEdgeSharp *)py_fe)->py_fe.py_if1D.if1D = ((BPy_FEdgeSharp *)py_fe)->fes;
- ((BPy_FEdgeSharp *)py_fe)->py_fe.py_if1D.borrowed = 1;
+ ((BPy_FEdgeSharp *)py_fe)->py_fe.py_if1D.borrowed = true;
return py_fe;
}
@@ -238,7 +238,7 @@ PyObject *BPy_FEdgeSmooth_from_FEdgeSmooth(FEdgeSmooth& fes)
((BPy_FEdgeSmooth *)py_fe)->fes = &fes;
((BPy_FEdgeSmooth *)py_fe)->py_fe.fe = ((BPy_FEdgeSmooth *)py_fe)->fes;
((BPy_FEdgeSmooth *)py_fe)->py_fe.py_if1D.if1D = ((BPy_FEdgeSmooth *)py_fe)->fes;
- ((BPy_FEdgeSmooth *)py_fe)->py_fe.py_if1D.borrowed = 1;
+ ((BPy_FEdgeSmooth *)py_fe)->py_fe.py_if1D.borrowed = true;
return py_fe;
}
@@ -247,7 +247,7 @@ PyObject *BPy_FEdge_from_FEdge(FEdge& fe)
PyObject *py_fe = FEdge_Type.tp_new(&FEdge_Type, 0, 0);
((BPy_FEdge *)py_fe)->fe = &fe;
((BPy_FEdge *)py_fe)->py_if1D.if1D = ((BPy_FEdge *)py_fe)->fe;
- ((BPy_FEdge *)py_fe)->py_if1D.borrowed = 1;
+ ((BPy_FEdge *)py_fe)->py_if1D.borrowed = true;
return py_fe;
}
@@ -265,7 +265,7 @@ PyObject *BPy_Stroke_from_Stroke(Stroke& s)
PyObject *py_s = Stroke_Type.tp_new(&Stroke_Type, 0, 0);
((BPy_Stroke *)py_s)->s = &s;
((BPy_Stroke *)py_s)->py_if1D.if1D = ((BPy_Stroke *)py_s)->s;
- ((BPy_Stroke *)py_s)->py_if1D.borrowed = 1;
+ ((BPy_Stroke *)py_s)->py_if1D.borrowed = true;
return py_s;
}
@@ -273,7 +273,7 @@ PyObject *BPy_StrokeAttribute_from_StrokeAttribute(StrokeAttribute& sa)
{
PyObject *py_sa = StrokeAttribute_Type.tp_new(&StrokeAttribute_Type, 0, 0);
((BPy_StrokeAttribute *)py_sa)->sa = &sa;
- ((BPy_StrokeAttribute *)py_sa)->borrowed = 1;
+ ((BPy_StrokeAttribute *)py_sa)->borrowed = true;
return py_sa;
}
@@ -292,7 +292,7 @@ PyObject *BPy_StrokeVertex_from_StrokeVertex(StrokeVertex& sv)
((BPy_StrokeVertex *)py_sv)->sv = &sv;
((BPy_StrokeVertex *)py_sv)->py_cp.cp = ((BPy_StrokeVertex *)py_sv)->sv;
((BPy_StrokeVertex *)py_sv)->py_cp.py_if0D.if0D = ((BPy_StrokeVertex *)py_sv)->sv;
- ((BPy_StrokeVertex *)py_sv)->py_cp.py_if0D.borrowed = 1;
+ ((BPy_StrokeVertex *)py_sv)->py_cp.py_if0D.borrowed = true;
return py_sv;
}
@@ -301,7 +301,7 @@ PyObject *BPy_ViewVertex_from_ViewVertex(ViewVertex& vv)
PyObject *py_vv = ViewVertex_Type.tp_new(&ViewVertex_Type, 0, 0);
((BPy_ViewVertex *)py_vv)->vv = &vv;
((BPy_ViewVertex *)py_vv)->py_if0D.if0D = ((BPy_ViewVertex *)py_vv)->vv;
- ((BPy_ViewVertex *)py_vv)->py_if0D.borrowed = 1;
+ ((BPy_ViewVertex *)py_vv)->py_if0D.borrowed = true;
return py_vv;
}
@@ -311,7 +311,7 @@ PyObject *BPy_NonTVertex_from_NonTVertex(NonTVertex& ntv)
((BPy_NonTVertex *)py_ntv)->ntv = &ntv;
((BPy_NonTVertex *)py_ntv)->py_vv.vv = ((BPy_NonTVertex *)py_ntv)->ntv;
((BPy_NonTVertex *)py_ntv)->py_vv.py_if0D.if0D = ((BPy_NonTVertex *)py_ntv)->ntv;
- ((BPy_NonTVertex *)py_ntv)->py_vv.py_if0D.borrowed = 1;
+ ((BPy_NonTVertex *)py_ntv)->py_vv.py_if0D.borrowed = true;
return py_ntv;
}
@@ -321,7 +321,7 @@ PyObject *BPy_TVertex_from_TVertex(TVertex& tv)
((BPy_TVertex *)py_tv)->tv = &tv;
((BPy_TVertex *)py_tv)->py_vv.vv = ((BPy_TVertex *)py_tv)->tv;
((BPy_TVertex *)py_tv)->py_vv.py_if0D.if0D = ((BPy_TVertex *)py_tv)->tv;
- ((BPy_TVertex *)py_tv)->py_vv.py_if0D.borrowed = 1;
+ ((BPy_TVertex *)py_tv)->py_vv.py_if0D.borrowed = true;
return py_tv;
}
@@ -337,7 +337,7 @@ PyObject *BPy_ViewEdge_from_ViewEdge(ViewEdge& ve)
PyObject *py_ve = ViewEdge_Type.tp_new(&ViewEdge_Type, 0, 0);
((BPy_ViewEdge *)py_ve)->ve = &ve;
((BPy_ViewEdge *)py_ve)->py_if1D.if1D = ((BPy_ViewEdge *)py_ve)->ve;
- ((BPy_ViewEdge *)py_ve)->py_if1D.borrowed = 1;
+ ((BPy_ViewEdge *)py_ve)->py_if1D.borrowed = true;
return py_ve;
}
@@ -347,7 +347,7 @@ PyObject *BPy_Chain_from_Chain(Chain& c)
((BPy_Chain *)py_c)->c = &c;
((BPy_Chain *)py_c)->py_c.c = ((BPy_Chain *)py_c)->c;
((BPy_Chain *)py_c)->py_c.py_if1D.if1D = ((BPy_Chain *)py_c)->c;
- ((BPy_Chain *)py_c)->py_c.py_if1D.borrowed = 1;
+ ((BPy_Chain *)py_c)->py_c.py_if1D.borrowed = true;
return py_c;
}
@@ -355,7 +355,7 @@ PyObject *BPy_SShape_from_SShape(SShape& ss)
{
PyObject *py_ss = SShape_Type.tp_new(&SShape_Type, 0, 0);
((BPy_SShape *)py_ss)->ss = &ss;
- ((BPy_SShape *)py_ss)->borrowed = 1;
+ ((BPy_SShape *)py_ss)->borrowed = true;
return py_ss;
}
@@ -363,7 +363,7 @@ PyObject *BPy_ViewShape_from_ViewShape(ViewShape& vs)
{
PyObject *py_vs = ViewShape_Type.tp_new(&ViewShape_Type, 0, 0);
((BPy_ViewShape *)py_vs)->vs = &vs;
- ((BPy_ViewShape *)py_vs)->borrowed = 1;
+ ((BPy_ViewShape *)py_vs)->borrowed = true;
((BPy_ViewShape *)py_vs)->py_ss = NULL;
return py_vs;
}
@@ -389,7 +389,7 @@ PyObject *BPy_CurvePoint_from_CurvePoint(CurvePoint& cp)
PyObject *py_cp = CurvePoint_Type.tp_new(&CurvePoint_Type, 0, 0);
((BPy_CurvePoint *) py_cp)->cp = &cp;
((BPy_CurvePoint *) py_cp)->py_if0D.if0D = ((BPy_CurvePoint *)py_cp)->cp;
- ((BPy_CurvePoint *) py_cp)->py_if0D.borrowed = 1;
+ ((BPy_CurvePoint *) py_cp)->py_if0D.borrowed = true;
return py_cp;
}
@@ -718,7 +718,7 @@ bool Vec3r_ptr_from_PyTuple(PyObject *obj, Vec3r &vec)
return true;
}
-// helper for argument parsing
+// helpers for argument parsing
bool float_array_from_PyObject(PyObject *obj, float *v, int n)
{
@@ -745,6 +745,22 @@ bool float_array_from_PyObject(PyObject *obj, float *v, int n)
return 0;
}
+int convert_v4(PyObject *obj, void *v)
+{
+ return mathutils_array_parse((float *)v, 4, 4, obj, "Error parsing 4D vector");
+}
+
+int convert_v3(PyObject *obj, void *v)
+{
+ return mathutils_array_parse((float *)v, 3, 3, obj, "Error parsing 3D vector");
+}
+
+int convert_v2(PyObject *obj, void *v)
+{
+ return mathutils_array_parse((float *)v, 2, 2, obj, "Error parsing 2D vector");
+}
+
+
///////////////////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
diff --git a/source/blender/freestyle/intern/python/BPy_Convert.h b/source/blender/freestyle/intern/python/BPy_Convert.h
index cf55ba335cd..e6e763e763e 100644
--- a/source/blender/freestyle/intern/python/BPy_Convert.h
+++ b/source/blender/freestyle/intern/python/BPy_Convert.h
@@ -170,6 +170,9 @@ bool Vec3r_ptr_from_PyTuple(PyObject *obj, Vec3r &vec);
bool float_array_from_PyObject(PyObject *obj, float *v, int n);
+int convert_v4(PyObject *obj, void *v);
+int convert_v3(PyObject *obj, void *v);
+int convert_v2(PyObject *obj, void *v);
///////////////////////////////////////////////////////////////////////////////////////////
diff --git a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp
index f390e937aac..2cfd3658189 100644
--- a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp
+++ b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp
@@ -138,14 +138,16 @@ static PyObject *Freestyle_blendRamp(PyObject *self, PyObject *args)
PyErr_SetString(PyExc_TypeError, "argument 1 is an unknown ramp blend type");
return NULL;
}
- if (!float_array_from_PyObject(obj1, a, 3)) {
- PyErr_SetString(PyExc_TypeError,
- "argument 2 must be a 3D vector (either a tuple/list of 3 elements or Vector)");
+ if (mathutils_array_parse(a, 3, 3, obj1,
+ "argument 2 must be a 3D vector "
+ "(either a tuple/list of 3 elements or Vector)") == -1)
+ {
return NULL;
}
- if (!float_array_from_PyObject(obj2, b, 3)) {
- PyErr_SetString(PyExc_TypeError,
- "argument 4 must be a 3D vector (either a tuple/list of 3 elements or Vector)");
+ if (mathutils_array_parse(b, 3, 3, obj2,
+ "argument 4 must be a 3D vector "
+ "(either a tuple/list of 3 elements or Vector)") == -1)
+ {
return NULL;
}
ramp_blend(type, a, fac, b);
diff --git a/source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp b/source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp
index 7fe03dcc9a0..f967fc64ac6 100644
--- a/source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp
+++ b/source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp
@@ -80,11 +80,6 @@ PyDoc_STRVAR(FrsMaterial_doc,
" :arg shininess: The shininess coefficient.\n"
" :type shininess: :class:float");
-static int convert_v4(PyObject *obj, void *v)
-{
- return float_array_from_PyObject(obj, (float *)v, 4);
-}
-
static int FrsMaterial_init(BPy_FrsMaterial *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist_1[] = {"brother", NULL};
@@ -299,8 +294,9 @@ static PyObject *FrsMaterial_diffuse_get(BPy_FrsMaterial *self, void *UNUSED(clo
static int FrsMaterial_diffuse_set(BPy_FrsMaterial *self, PyObject *value, void *UNUSED(closure))
{
float color[4];
- if (!float_array_from_PyObject(value, color, 4)) {
- PyErr_SetString(PyExc_ValueError, "value must be a 4-dimensional vector");
+ if (mathutils_array_parse(color, 4, 4, value,
+ "value must be a 4-dimensional vector") == -1)
+ {
return -1;
}
self->m->setDiffuse(color[0], color[1], color[2], color[3]);
@@ -320,8 +316,9 @@ static PyObject *FrsMaterial_specular_get(BPy_FrsMaterial *self, void *UNUSED(cl
static int FrsMaterial_specular_set(BPy_FrsMaterial *self, PyObject *value, void *UNUSED(closure))
{
float color[4];
- if (!float_array_from_PyObject(value, color, 4)) {
- PyErr_SetString(PyExc_ValueError, "value must be a 4-dimensional vector");
+ if (mathutils_array_parse(color, 4, 4, value,
+ "value must be a 4-dimensional vector") == -1)
+ {
return -1;
}
self->m->setSpecular(color[0], color[1], color[2], color[3]);
@@ -341,8 +338,9 @@ static PyObject *FrsMaterial_ambient_get(BPy_FrsMaterial *self, void *UNUSED(clo
static int FrsMaterial_ambient_set(BPy_FrsMaterial *self, PyObject *value, void *UNUSED(closure))
{
float color[4];
- if (!float_array_from_PyObject(value, color, 4)) {
- PyErr_SetString(PyExc_ValueError, "value must be a 4-dimensional vector");
+ if (mathutils_array_parse(color, 4, 4, value,
+ "value must be a 4-dimensional vector") == -1)
+ {
return -1;
}
self->m->setAmbient(color[0], color[1], color[2], color[3]);
@@ -362,8 +360,9 @@ static PyObject *FrsMaterial_emission_get(BPy_FrsMaterial *self, void *UNUSED(cl
static int FrsMaterial_emission_set(BPy_FrsMaterial *self, PyObject *value, void *UNUSED(closure))
{
float color[4];
- if (!float_array_from_PyObject(value, color, 4)) {
- PyErr_SetString(PyExc_ValueError, "value must be a 4-dimensional vector");
+ if (mathutils_array_parse(color, 4, 4, value,
+ "value must be a 4-dimensional vector") == -1)
+ {
return -1;
}
self->m->setEmission(color[0], color[1], color[2], color[3]);
diff --git a/source/blender/freestyle/intern/python/BPy_Interface0D.cpp b/source/blender/freestyle/intern/python/BPy_Interface0D.cpp
index 0861952689b..9355c9cd677 100644
--- a/source/blender/freestyle/intern/python/BPy_Interface0D.cpp
+++ b/source/blender/freestyle/intern/python/BPy_Interface0D.cpp
@@ -103,7 +103,7 @@ static int Interface0D_init(BPy_Interface0D *self, PyObject *args, PyObject *kwd
if (!PyArg_ParseTupleAndKeywords(args, kwds, "", (char **)kwlist))
return -1;
self->if0D = new Interface0D();
- self->borrowed = 0;
+ self->borrowed = false;
return 0;
}
diff --git a/source/blender/freestyle/intern/python/BPy_Interface0D.h b/source/blender/freestyle/intern/python/BPy_Interface0D.h
index b8a4a6b21bd..ec1a6f1c42d 100644
--- a/source/blender/freestyle/intern/python/BPy_Interface0D.h
+++ b/source/blender/freestyle/intern/python/BPy_Interface0D.h
@@ -47,7 +47,7 @@ extern PyTypeObject Interface0D_Type;
typedef struct {
PyObject_HEAD
Interface0D *if0D;
- int borrowed; /* non-zero if *if0D is a borrowed object */
+ bool borrowed; /* true if *if0D is a borrowed object */
} BPy_Interface0D;
/*---------------------------Python BPy_Interface0D visible prototypes-----------*/
diff --git a/source/blender/freestyle/intern/python/BPy_Interface1D.cpp b/source/blender/freestyle/intern/python/BPy_Interface1D.cpp
index a4bcea869fd..0fc3ec41dec 100644
--- a/source/blender/freestyle/intern/python/BPy_Interface1D.cpp
+++ b/source/blender/freestyle/intern/python/BPy_Interface1D.cpp
@@ -113,7 +113,7 @@ static int Interface1D_init(BPy_Interface1D *self, PyObject *args, PyObject *kwd
if (!PyArg_ParseTupleAndKeywords(args, kwds, "", (char **)kwlist))
return -1;
self->if1D = new Interface1D();
- self->borrowed = 0;
+ self->borrowed = false;
return 0;
}
@@ -141,7 +141,7 @@ PyDoc_STRVAR(Interface1D_vertices_begin_doc,
static PyObject * Interface1D_vertices_begin(BPy_Interface1D *self)
{
Interface0DIterator if0D_it(self->if1D->verticesBegin());
- return BPy_Interface0DIterator_from_Interface0DIterator(if0D_it, 0);
+ return BPy_Interface0DIterator_from_Interface0DIterator(if0D_it, false);
}
PyDoc_STRVAR(Interface1D_vertices_end_doc,
@@ -156,7 +156,7 @@ PyDoc_STRVAR(Interface1D_vertices_end_doc,
static PyObject * Interface1D_vertices_end(BPy_Interface1D *self)
{
Interface0DIterator if0D_it(self->if1D->verticesEnd());
- return BPy_Interface0DIterator_from_Interface0DIterator(if0D_it, 1);
+ return BPy_Interface0DIterator_from_Interface0DIterator(if0D_it, true);
}
PyDoc_STRVAR(Interface1D_points_begin_doc,
@@ -181,7 +181,7 @@ static PyObject * Interface1D_points_begin(BPy_Interface1D *self, PyObject *args
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|f", (char **)kwlist, &f))
return NULL;
Interface0DIterator if0D_it(self->if1D->pointsBegin(f));
- return BPy_Interface0DIterator_from_Interface0DIterator(if0D_it, 0);
+ return BPy_Interface0DIterator_from_Interface0DIterator(if0D_it, false);
}
PyDoc_STRVAR(Interface1D_points_end_doc,
@@ -206,7 +206,7 @@ static PyObject * Interface1D_points_end(BPy_Interface1D *self, PyObject *args,
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|f", (char **)kwlist, &f))
return NULL;
Interface0DIterator if0D_it(self->if1D->pointsEnd(f));
- return BPy_Interface0DIterator_from_Interface0DIterator(if0D_it, 1);
+ return BPy_Interface0DIterator_from_Interface0DIterator(if0D_it, true);
}
static PyMethodDef BPy_Interface1D_methods[] = {
diff --git a/source/blender/freestyle/intern/python/BPy_Interface1D.h b/source/blender/freestyle/intern/python/BPy_Interface1D.h
index 17c0752b2e8..0c731bb1755 100644
--- a/source/blender/freestyle/intern/python/BPy_Interface1D.h
+++ b/source/blender/freestyle/intern/python/BPy_Interface1D.h
@@ -47,7 +47,7 @@ extern PyTypeObject Interface1D_Type;
typedef struct {
PyObject_HEAD
Interface1D *if1D;
- int borrowed; /* non-zero if *if1D is a borrowed object */
+ bool borrowed; /* true if *if1D is a borrowed object */
} BPy_Interface1D;
/*---------------------------Python BPy_Interface1D visible prototypes-----------*/
diff --git a/source/blender/freestyle/intern/python/BPy_SShape.cpp b/source/blender/freestyle/intern/python/BPy_SShape.cpp
index ffa9dc47e67..e5a38171ecd 100644
--- a/source/blender/freestyle/intern/python/BPy_SShape.cpp
+++ b/source/blender/freestyle/intern/python/BPy_SShape.cpp
@@ -78,7 +78,7 @@ static int SShape_init(BPy_SShape *self, PyObject *args, PyObject *kwds)
self->ss = new SShape();
else
self->ss = new SShape(*(((BPy_SShape *)brother)->ss));
- self->borrowed = 0;
+ self->borrowed = false;
return 0;
}
diff --git a/source/blender/freestyle/intern/python/BPy_SShape.h b/source/blender/freestyle/intern/python/BPy_SShape.h
index 4919a34bf6d..2bfec42cbdd 100644
--- a/source/blender/freestyle/intern/python/BPy_SShape.h
+++ b/source/blender/freestyle/intern/python/BPy_SShape.h
@@ -47,7 +47,7 @@ extern PyTypeObject SShape_Type;
typedef struct {
PyObject_HEAD
SShape *ss;
- int borrowed; /* non-zero if *ss is a borrowed object */
+ bool borrowed; /* true if *ss is a borrowed object */
} BPy_SShape;
/*---------------------------Python BPy_SShape visible prototypes-----------*/
diff --git a/source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp b/source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp
index f36cbafa61a..22ffdedb3d9 100644
--- a/source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp
+++ b/source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp
@@ -124,7 +124,7 @@ static int StrokeAttribute_init(BPy_StrokeAttribute *self, PyObject *args, PyObj
PyErr_SetString(PyExc_TypeError, "invalid argument(s)");
return -1;
}
- self->borrowed = 0;
+ self->borrowed = false;
return 0;
}
@@ -525,8 +525,9 @@ static PyObject *StrokeAttribute_color_get(BPy_StrokeAttribute *self, void *UNUS
static int StrokeAttribute_color_set(BPy_StrokeAttribute *self, PyObject *value, void *UNUSED(closure))
{
float v[3];
- if (!float_array_from_PyObject(value, v, 3)) {
- PyErr_SetString(PyExc_ValueError, "value must be a 3-dimensional vector");
+ if (mathutils_array_parse(v, 3, 3, value,
+ "value must be a 3-dimensional vector") == -1)
+ {
return -1;
}
self->sa->setColor(v[0], v[1], v[2]);
@@ -549,8 +550,9 @@ static PyObject *StrokeAttribute_thickness_get(BPy_StrokeAttribute *self, void *
static int StrokeAttribute_thickness_set(BPy_StrokeAttribute *self, PyObject *value, void *UNUSED(closure))
{
float v[2];
- if (!float_array_from_PyObject(value, v, 2)) {
- PyErr_SetString(PyExc_ValueError, "value must be a 2-dimensional vector");
+ if (mathutils_array_parse(v, 2, 2, value,
+ "value must be a 2-dimensional vector") == -1)
+ {
return -1;
}
self->sa->setThickness(v[0], v[1]);
diff --git a/source/blender/freestyle/intern/python/BPy_StrokeAttribute.h b/source/blender/freestyle/intern/python/BPy_StrokeAttribute.h
index ce2384ea7d6..48a61cc9f07 100644
--- a/source/blender/freestyle/intern/python/BPy_StrokeAttribute.h
+++ b/source/blender/freestyle/intern/python/BPy_StrokeAttribute.h
@@ -47,7 +47,7 @@ extern PyTypeObject StrokeAttribute_Type;
typedef struct {
PyObject_HEAD
StrokeAttribute *sa;
- int borrowed; /* non-zero if *sa is a borrowed reference */
+ bool borrowed; /* true if *sa is a borrowed reference */
} BPy_StrokeAttribute;
/*---------------------------Python BPy_StrokeAttribute visible prototypes-----------*/
diff --git a/source/blender/freestyle/intern/python/BPy_ViewShape.cpp b/source/blender/freestyle/intern/python/BPy_ViewShape.cpp
index 93d4607e15a..2c767eacaec 100644
--- a/source/blender/freestyle/intern/python/BPy_ViewShape.cpp
+++ b/source/blender/freestyle/intern/python/BPy_ViewShape.cpp
@@ -101,7 +101,7 @@ static int ViewShape_init(BPy_ViewShape *self, PyObject *args, PyObject *kwds)
PyErr_SetString(PyExc_TypeError, "invalid argument(s)");
return -1;
}
- self->borrowed = 0;
+ self->borrowed = false;
Py_XINCREF(self->py_ss);
return 0;
}
diff --git a/source/blender/freestyle/intern/python/BPy_ViewShape.h b/source/blender/freestyle/intern/python/BPy_ViewShape.h
index 3151ebe8cbd..09bf36edfcd 100644
--- a/source/blender/freestyle/intern/python/BPy_ViewShape.h
+++ b/source/blender/freestyle/intern/python/BPy_ViewShape.h
@@ -49,7 +49,7 @@ extern PyTypeObject ViewShape_Type;
typedef struct {
PyObject_HEAD
ViewShape *vs;
- int borrowed; /* non-zero if *vs a borrowed object */
+ bool borrowed; /* true if *vs a borrowed object */
BPy_SShape *py_ss;
} BPy_ViewShape;
diff --git a/source/blender/freestyle/intern/python/Director.cpp b/source/blender/freestyle/intern/python/Director.cpp
index f03aa0bcc97..db287fd054d 100644
--- a/source/blender/freestyle/intern/python/Director.cpp
+++ b/source/blender/freestyle/intern/python/Director.cpp
@@ -121,7 +121,7 @@ int Director_BPy_UnaryPredicate0D___call__(UnaryPredicate0D *up0D, Interface0DIt
PyErr_SetString(PyExc_RuntimeError, "Reference to Python object (py_up0D) not initialized");
return -1;
}
- PyObject *arg = BPy_Interface0DIterator_from_Interface0DIterator(if0D_it, 0);
+ PyObject *arg = BPy_Interface0DIterator_from_Interface0DIterator(if0D_it, false);
if (!arg)
return -1;
PyObject *result = PyObject_CallMethod((PyObject *)up0D->py_up0D, (char *)"__call__", (char *)"O", arg);
@@ -226,7 +226,7 @@ int Director_BPy_UnaryFunction0D___call__(void *uf0D, void *py_uf0D, Interface0D
return -1;
}
PyObject *obj = (PyObject *)py_uf0D;
- PyObject *arg = BPy_Interface0DIterator_from_Interface0DIterator(if0D_it, 0);
+ PyObject *arg = BPy_Interface0DIterator_from_Interface0DIterator(if0D_it, false);
if (!arg)
return -1;
PyObject *result = PyObject_CallMethod(obj, (char *)"__call__", (char *)"O", arg);
diff --git a/source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp b/source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp
index 50c9d031d0d..36cdb1b92ff 100644
--- a/source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp
+++ b/source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp
@@ -124,7 +124,7 @@ static int CurvePoint_init(BPy_CurvePoint *self, PyObject *args, PyObject *kwds)
return -1;
}
self->py_if0D.if0D = self->cp;
- self->py_if0D.borrowed = 0;
+ self->py_if0D.borrowed = false;
return 0;
}
diff --git a/source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp b/source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp
index 7ce34142c77..af9f7198748 100644
--- a/source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp
+++ b/source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp
@@ -61,11 +61,6 @@ PyDoc_STRVAR(SVertex_doc,
" :arg id: An Id object.\n"
" :type id: :class:`Id`");
-static int convert_v3(PyObject *obj, void *v)
-{
- return float_array_from_PyObject(obj, (float *)v, 3);
-}
-
static int SVertex_init(BPy_SVertex *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist_1[] = {"brother", NULL};
@@ -90,7 +85,7 @@ static int SVertex_init(BPy_SVertex *self, PyObject *args, PyObject *kwds)
return -1;
}
self->py_if0D.if0D = self->sv;
- self->py_if0D.borrowed = 0;
+ self->py_if0D.borrowed = false;
return 0;
}
@@ -283,8 +278,9 @@ static PyObject *SVertex_point_3d_get(BPy_SVertex *self, void *UNUSED(closure))
static int SVertex_point_3d_set(BPy_SVertex *self, PyObject *value, void *UNUSED(closure))
{
float v[3];
- if (!float_array_from_PyObject(value, v, 3)) {
- PyErr_SetString(PyExc_ValueError, "value must be a 3-dimensional vector");
+ if (mathutils_array_parse(v, 3, 3, value,
+ "value must be a 3-dimensional vector") == -1)
+ {
return -1;
}
Vec3r p(v[0], v[1], v[2]);
@@ -305,8 +301,9 @@ static PyObject *SVertex_point_2d_get(BPy_SVertex *self, void *UNUSED(closure))
static int SVertex_point_2d_set(BPy_SVertex *self, PyObject *value, void *UNUSED(closure))
{
float v[3];
- if (!float_array_from_PyObject(value, v, 3)) {
- PyErr_SetString(PyExc_ValueError, "value must be a 3-dimensional vector");
+ if (mathutils_array_parse(v, 3, 3, value,
+ "value must be a 3-dimensional vector") == -1)
+ {
return -1;
}
Vec3r p(v[0], v[1], v[2]);
diff --git a/source/blender/freestyle/intern/python/Interface0D/BPy_ViewVertex.cpp b/source/blender/freestyle/intern/python/Interface0D/BPy_ViewVertex.cpp
index 0b200dbf3ca..5e2130ac8e7 100644
--- a/source/blender/freestyle/intern/python/Interface0D/BPy_ViewVertex.cpp
+++ b/source/blender/freestyle/intern/python/Interface0D/BPy_ViewVertex.cpp
@@ -70,7 +70,7 @@ PyDoc_STRVAR(ViewVertex_edges_begin_doc,
static PyObject *ViewVertex_edges_begin(BPy_ViewVertex *self)
{
ViewVertexInternal::orientedViewEdgeIterator ove_it(self->vv->edgesBegin());
- return BPy_orientedViewEdgeIterator_from_orientedViewEdgeIterator(ove_it, 0);
+ return BPy_orientedViewEdgeIterator_from_orientedViewEdgeIterator(ove_it, false);
}
PyDoc_STRVAR(ViewVertex_edges_end_doc,
@@ -113,7 +113,7 @@ static PyObject *ViewVertex_edges_iterator(BPy_ViewVertex *self, PyObject *args,
return NULL;
ViewEdge *ve = ((BPy_ViewEdge *)py_ve)->ve;
ViewVertexInternal::orientedViewEdgeIterator ove_it(self->vv->edgesIterator(ve));
- return BPy_orientedViewEdgeIterator_from_orientedViewEdgeIterator(ove_it, 0);
+ return BPy_orientedViewEdgeIterator_from_orientedViewEdgeIterator(ove_it, false);
}
static PyMethodDef BPy_ViewVertex_methods[] = {
diff --git a/source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp b/source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp
index e08aa47d954..65d80283fca 100644
--- a/source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp
+++ b/source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp
@@ -150,7 +150,7 @@ static int StrokeVertex_init(BPy_StrokeVertex *self, PyObject *args, PyObject *k
}
self->py_cp.cp = self->sv;
self->py_cp.py_if0D.if0D = self->sv;
- self->py_cp.py_if0D.borrowed = 0;
+ self->py_cp.py_if0D.borrowed = false;
return 0;
}
@@ -277,8 +277,9 @@ static PyObject *StrokeVertex_point_get(BPy_StrokeVertex *self, void *UNUSED(clo
static int StrokeVertex_point_set(BPy_StrokeVertex *self, PyObject *value, void *UNUSED(closure))
{
float v[2];
- if (!float_array_from_PyObject(value, v, 2)) {
- PyErr_SetString(PyExc_ValueError, "value must be a 2-dimensional vector");
+ if (mathutils_array_parse(v, 2, 2, value,
+ "value must be a 2-dimensional vector") == -1)
+ {
return -1;
}
self->sv->setX(v[0]);
diff --git a/source/blender/freestyle/intern/python/Interface0D/ViewVertex/BPy_NonTVertex.cpp b/source/blender/freestyle/intern/python/Interface0D/ViewVertex/BPy_NonTVertex.cpp
index 985c89b5c36..555f93effa0 100644
--- a/source/blender/freestyle/intern/python/Interface0D/ViewVertex/BPy_NonTVertex.cpp
+++ b/source/blender/freestyle/intern/python/Interface0D/ViewVertex/BPy_NonTVertex.cpp
@@ -67,7 +67,7 @@ static int NonTVertex_init(BPy_NonTVertex *self, PyObject *args, PyObject *kwds)
self->ntv = new NonTVertex(((BPy_SVertex *)obj)->sv);
self->py_vv.vv = self->ntv;
self->py_vv.py_if0D.if0D = self->ntv;
- self->py_vv.py_if0D.borrowed = 0;
+ self->py_vv.py_if0D.borrowed = false;
return 0;
}
diff --git a/source/blender/freestyle/intern/python/Interface0D/ViewVertex/BPy_TVertex.cpp b/source/blender/freestyle/intern/python/Interface0D/ViewVertex/BPy_TVertex.cpp
index 2881148f241..d047d9ae914 100644
--- a/source/blender/freestyle/intern/python/Interface0D/ViewVertex/BPy_TVertex.cpp
+++ b/source/blender/freestyle/intern/python/Interface0D/ViewVertex/BPy_TVertex.cpp
@@ -62,7 +62,7 @@ static int TVertex_init(BPy_TVertex *self, PyObject *args, PyObject *kwds)
self->tv = new TVertex();
self->py_vv.vv = self->tv;
self->py_vv.py_if0D.if0D = self->tv;
- self->py_vv.py_if0D.borrowed = 0;
+ self->py_vv.py_if0D.borrowed = false;
return 0;
}
diff --git a/source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp b/source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp
index 7365ee8ac36..7592508902b 100644
--- a/source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp
+++ b/source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp
@@ -94,7 +94,7 @@ static int FEdge_init(BPy_FEdge *self, PyObject *args, PyObject *kwds)
return -1;
}
self->py_if1D.if1D = self->fe;
- self->py_if1D.borrowed = 0;
+ self->py_if1D.borrowed = false;
return 0;
}
diff --git a/source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp b/source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp
index 9bbb0405e49..744556e415c 100644
--- a/source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp
+++ b/source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp
@@ -84,7 +84,7 @@ static int FrsCurve_init(BPy_FrsCurve *self, PyObject *args, PyObject *kwds)
return -1;
}
self->py_if1D.if1D = self->c;
- self->py_if1D.borrowed = 0;
+ self->py_if1D.borrowed = false;
return 0;
}
diff --git a/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp b/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp
index aa8c90cfd24..5c816bdfea1 100644
--- a/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp
+++ b/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp
@@ -73,14 +73,14 @@ static int Stroke_init(BPy_Stroke *self, PyObject *args, PyObject *kwds)
else
self->s = new Stroke(*(((BPy_Stroke *)brother)->s));
self->py_if1D.if1D = self->s;
- self->py_if1D.borrowed = 0;
+ self->py_if1D.borrowed = false;
return 0;
}
static PyObject *Stroke_iter(PyObject *self)
{
StrokeInternal::StrokeVertexIterator sv_it( ((BPy_Stroke *)self)->s->strokeVerticesBegin() );
- return BPy_StrokeVertexIterator_from_StrokeVertexIterator( sv_it, 0 );
+ return BPy_StrokeVertexIterator_from_StrokeVertexIterator(sv_it, false);
}
static Py_ssize_t Stroke_sq_length(BPy_Stroke *self)
@@ -192,7 +192,7 @@ static PyObject *Stroke_insert_vertex(BPy_Stroke *self, PyObject *args, PyObject
{
return NULL;
}
- ((BPy_StrokeVertex *)py_sv)->py_cp.py_if0D.borrowed = 1; /* make the wrapped StrokeVertex internal */
+ ((BPy_StrokeVertex *)py_sv)->py_cp.py_if0D.borrowed = true; /* make the wrapped StrokeVertex internal */
StrokeVertex *sv = ((BPy_StrokeVertex *)py_sv)->sv;
StrokeInternal::StrokeVertexIterator sv_it(*(((BPy_StrokeVertexIterator *)py_sv_it)->sv_it));
self->s->InsertVertex(sv, sv_it);
@@ -286,6 +286,21 @@ static PyObject *Stroke_stroke_vertices_end(BPy_Stroke *self)
return BPy_StrokeVertexIterator_from_StrokeVertexIterator(sv_it, true);
}
+PyDoc_STRVAR(Stroke_reversed_doc,
+".. method:: __reversed__()\n"
+"\n"
+" Returns a StrokeVertexIterator iterating over the vertices of the Stroke\n"
+" in the reversed order (from the last to the first).\n"
+"\n"
+" :return: A StrokeVertexIterator pointing after the last StrokeVertex.\n"
+" :rtype: :class:`StrokeVertexIterator`");
+
+static PyObject *Stroke_reversed(BPy_Stroke *self)
+{
+ StrokeInternal::StrokeVertexIterator sv_it(self->s->strokeVerticesEnd());
+ return BPy_StrokeVertexIterator_from_StrokeVertexIterator(sv_it, true);
+}
+
PyDoc_STRVAR(Stroke_stroke_vertices_size_doc,
".. method:: stroke_vertices_size()\n"
"\n"
@@ -310,6 +325,7 @@ static PyMethodDef BPy_Stroke_methods[] = {
{"stroke_vertices_begin", (PyCFunction)Stroke_stroke_vertices_begin, METH_VARARGS | METH_KEYWORDS,
Stroke_stroke_vertices_begin_doc},
{"stroke_vertices_end", (PyCFunction)Stroke_stroke_vertices_end, METH_NOARGS, Stroke_stroke_vertices_end_doc},
+ {"__reversed__", (PyCFunction)Stroke_reversed, METH_NOARGS, Stroke_reversed_doc},
{"stroke_vertices_size", (PyCFunction)Stroke_stroke_vertices_size, METH_NOARGS, Stroke_stroke_vertices_size_doc},
{NULL, NULL, 0, NULL}
};
diff --git a/source/blender/freestyle/intern/python/Interface1D/BPy_ViewEdge.cpp b/source/blender/freestyle/intern/python/Interface1D/BPy_ViewEdge.cpp
index 869ada0d058..bcae5a2c2a4 100644
--- a/source/blender/freestyle/intern/python/Interface1D/BPy_ViewEdge.cpp
+++ b/source/blender/freestyle/intern/python/Interface1D/BPy_ViewEdge.cpp
@@ -70,7 +70,7 @@ static int ViewEdge_init(BPy_ViewEdge *self, PyObject *args, PyObject *kwds)
else
self->ve = new ViewEdge(*(((BPy_ViewEdge *)brother)->ve));
self->py_if1D.if1D = self->ve;
- self->py_if1D.borrowed = 0;
+ self->py_if1D.borrowed = false;
return 0;
}
diff --git a/source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp b/source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp
index 3a0141da153..2d62918e291 100644
--- a/source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp
+++ b/source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp
@@ -84,7 +84,7 @@ static int Chain_init(BPy_Chain *self, PyObject *args, PyObject *kwds)
}
self->py_c.c = self->c;
self->py_c.py_if1D.if1D = self->c;
- self->py_c.py_if1D.borrowed = 0;
+ self->py_c.py_if1D.borrowed = false;
return 0;
}
diff --git a/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp b/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp
index eeae10412fc..acdd5989511 100644
--- a/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp
+++ b/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp
@@ -89,7 +89,7 @@ static int FEdgeSharp_init(BPy_FEdgeSharp *self, PyObject *args, PyObject *kwds)
}
self->py_fe.fe = self->fes;
self->py_fe.py_if1D.if1D = self->fes;
- self->py_fe.py_if1D.borrowed = 0;
+ self->py_fe.py_if1D.borrowed = false;
return 0;
}
@@ -231,8 +231,9 @@ static PyObject *FEdgeSharp_normal_right_get(BPy_FEdgeSharp *self, void *UNUSED(
static int FEdgeSharp_normal_right_set(BPy_FEdgeSharp *self, PyObject *value, void *UNUSED(closure))
{
float v[3];
- if (!float_array_from_PyObject(value, v, 3)) {
- PyErr_SetString(PyExc_ValueError, "value must be a 3-dimensional vector");
+ if (mathutils_array_parse(v, 3, 3, value,
+ "value must be a 3-dimensional vector") == -1)
+ {
return -1;
}
Vec3r p(v[0], v[1], v[2]);
@@ -253,8 +254,9 @@ static PyObject *FEdgeSharp_normal_left_get(BPy_FEdgeSharp *self, void *UNUSED(c
static int FEdgeSharp_normal_left_set(BPy_FEdgeSharp *self, PyObject *value, void *UNUSED(closure))
{
float v[3];
- if (!float_array_from_PyObject(value, v, 3)) {
- PyErr_SetString(PyExc_ValueError, "value must be a 3-dimensional vector");
+ if (mathutils_array_parse(v, 3, 3, value,
+ "value must be a 3-dimensional vector") == -1)
+ {
return -1;
}
Vec3r p(v[0], v[1], v[2]);
diff --git a/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp b/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp
index 8d45c5b81c4..a2079c7d685 100644
--- a/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp
+++ b/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp
@@ -86,7 +86,7 @@ static int FEdgeSmooth_init(BPy_FEdgeSmooth *self, PyObject *args, PyObject *kwd
}
self->py_fe.fe = self->fes;
self->py_fe.py_if1D.if1D = self->fes;
- self->py_fe.py_if1D.borrowed = 0;
+ self->py_fe.py_if1D.borrowed = false;
return 0;
}
@@ -164,8 +164,9 @@ static PyObject *FEdgeSmooth_normal_get(BPy_FEdgeSmooth *self, void *UNUSED(clos
static int FEdgeSmooth_normal_set(BPy_FEdgeSmooth *self, PyObject *value, void *UNUSED(closure))
{
float v[3];
- if (!float_array_from_PyObject(value, v, 3)) {
- PyErr_SetString(PyExc_ValueError, "value must be a 3-dimensional vector");
+ if (mathutils_array_parse(v, 3, 3, value,
+ "value must be a 3-dimensional vector") == -1)
+ {
return -1;
}
Vec3r p(v[0], v[1], v[2]);
diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp
index a339e6653c6..faf99e90111 100644
--- a/source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp
+++ b/source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp
@@ -25,6 +25,7 @@
#include "BPy_Interface0DIterator.h"
#include "../BPy_Convert.h"
+#include "../BPy_Interface1D.h"
#ifdef __cplusplus
extern "C" {
@@ -70,9 +71,10 @@ static int convert_nested_it(PyObject *obj, void *v)
static int Interface0DIterator_init(BPy_Interface0DIterator *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist_1[] = {"it", NULL};
- static const char *kwlist_2[] = {"brother", NULL};
+ static const char *kwlist_2[] = {"inter", NULL};
+ static const char *kwlist_3[] = {"brother", NULL};
Interface0DIteratorNested *nested_it;
- PyObject *brother;
+ PyObject *brother, *inter;
if (PyArg_ParseTupleAndKeywords(args, kwds, "O&", (char **)kwlist_1, convert_nested_it, &nested_it)) {
self->if0D_it = new Interface0DIterator(nested_it->copy());
@@ -80,7 +82,14 @@ static int Interface0DIterator_init(BPy_Interface0DIterator *self, PyObject *arg
self->reversed = false;
}
else if (PyErr_Clear(),
- PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_2, &Interface0DIterator_Type, &brother))
+ PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_2, &Interface1D_Type, &inter))
+ {
+ self->if0D_it = new Interface0DIterator(((BPy_Interface1D *)inter)->if1D->verticesBegin());
+ self->at_start = true;
+ self->reversed = false;
+ }
+ else if (PyErr_Clear(),
+ PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_3, &Interface0DIterator_Type, &brother))
{
self->if0D_it = new Interface0DIterator(*(((BPy_Interface0DIterator *)brother)->if0D_it));
self->at_start = ((BPy_Interface0DIterator *)brother)->at_start;
diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp
index 3e5051049bd..63a2541d865 100644
--- a/source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp
+++ b/source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp
@@ -25,6 +25,7 @@
#include "BPy_StrokeVertexIterator.h"
#include "../BPy_Convert.h"
+#include "../Interface1D/BPy_Stroke.h"
#include "BPy_Interface0DIterator.h"
#ifdef __cplusplus
@@ -48,7 +49,7 @@ PyDoc_STRVAR(StrokeVertexIterator_doc,
"specialized StrokeVertex type. In this case, one should use a\n"
"StrokeVertexIterator. To call functions of the UnaryFuntion0D type,\n"
"a StrokeVertexIterator can be converted to an Interface0DIterator by\n"
-"by calling Interface0DIterator(it)."
+"by calling Interface0DIterator(it).\n"
"\n"
".. method:: __init__()\n"
"\n"
@@ -63,20 +64,27 @@ PyDoc_STRVAR(StrokeVertexIterator_doc,
static int StrokeVertexIterator_init(BPy_StrokeVertexIterator *self, PyObject *args, PyObject *kwds)
{
- static const char *kwlist[] = {"brother", NULL};
- PyObject *brother = 0;
+ static const char *kwlist_1[] = {"brother", NULL};
+ static const char *kwlist_2[] = {"stroke", NULL};
+ PyObject *brother = 0, *stroke = 0;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!", (char **)kwlist, &StrokeVertexIterator_Type, &brother))
- return -1;
- if (!brother) {
- self->sv_it = new StrokeInternal::StrokeVertexIterator();
+ if (PyArg_ParseTupleAndKeywords(args, kwds, "|O!", (char **)kwlist_1, &StrokeVertexIterator_Type, &brother)) {
+ self->sv_it = new StrokeInternal::StrokeVertexIterator(*(((BPy_StrokeVertexIterator *)brother)->sv_it));
+ self->reversed = ((BPy_StrokeVertexIterator *)brother)->reversed;
+ self->at_start = ((BPy_StrokeVertexIterator *)brother)->at_start;
+ }
+
+ else if (PyErr_Clear(),
+ PyArg_ParseTupleAndKeywords(args, kwds, "|O!", (char **)kwlist_2, &Stroke_Type, &stroke))
+ {
+ self->sv_it = new StrokeInternal::StrokeVertexIterator(((BPy_Stroke *)stroke)->s->strokeVerticesBegin());
self->reversed = false;
self->at_start = true;
}
else {
- self->sv_it = new StrokeInternal::StrokeVertexIterator(*(((BPy_StrokeVertexIterator *)brother)->sv_it));
- self->reversed = ((BPy_StrokeVertexIterator *)brother)->reversed;
- self->at_start = ((BPy_StrokeVertexIterator *)brother)->at_start;
+ self->sv_it = new StrokeInternal::StrokeVertexIterator();
+ self->reversed = false;
+ self->at_start = true;
}
self->py_it.it = self->sv_it;
return 0;
@@ -91,6 +99,12 @@ static PyObject *StrokeVertexIterator_iter(BPy_StrokeVertexIterator *self)
static PyObject *StrokeVertexIterator_iternext(BPy_StrokeVertexIterator *self)
{
+ /* Because Freestyle iterators for which it.isEnd() holds true have no valid object
+ * (referencing it.object in this case leads to a crash), we must check if it.object
+ * is valid after incrementing, to prevent crashes in Python.
+ * Additionally, the at_start attribute is used to keep Freestyle iterator objects
+ * and Python for loops in sync. */
+
if (self->reversed) {
if (self->sv_it->isBegin()) {
PyErr_SetNone(PyExc_StopIteration);
@@ -121,6 +135,69 @@ static PyObject *StrokeVertexIterator_iternext(BPy_StrokeVertexIterator *self)
return BPy_StrokeVertex_from_StrokeVertex(*sv);
}
+/*----------------------StrokeVertexIterator methods ----------------------------*/
+
+PyDoc_STRVAR(StrokeVertexIterator_incremented_doc,
+".. method:: incremented()\n"
+"\n"
+" Returns a copy of an incremented StrokeVertexIterator.\n"
+"\n"
+" :return: A StrokeVertexIterator pointing the next StrokeVertex.\n"
+" :rtype: :class:`StrokeVertexIterator`");
+
+static PyObject *StrokeVertexIterator_incremented(BPy_StrokeVertexIterator *self)
+{
+ if (self->sv_it->isEnd()) {
+ PyErr_SetString(PyExc_RuntimeError, "cannot increment any more");
+ return NULL;
+ }
+ StrokeInternal::StrokeVertexIterator *copy = new StrokeInternal::StrokeVertexIterator(*self->sv_it);
+ copy->increment();
+ return BPy_StrokeVertexIterator_from_StrokeVertexIterator(*copy, self->reversed);
+}
+
+PyDoc_STRVAR(StrokeVertexIterator_decremented_doc,
+".. method:: decremented()\n"
+"\n"
+" Returns a copy of a decremented StrokeVertexIterator.\n"
+"\n"
+" :return: A StrokeVertexIterator pointing the previous StrokeVertex.\n"
+" :rtype: :class:`StrokeVertexIterator`");
+
+static PyObject *StrokeVertexIterator_decremented(BPy_StrokeVertexIterator *self)
+{
+ if (self->sv_it->isBegin()) {
+ PyErr_SetString(PyExc_RuntimeError, "cannot decrement any more");
+ return NULL;
+ }
+
+ StrokeInternal::StrokeVertexIterator *copy = new StrokeInternal::StrokeVertexIterator(*self->sv_it);
+ copy->decrement();
+ return BPy_StrokeVertexIterator_from_StrokeVertexIterator(*copy, self->reversed);
+}
+
+PyDoc_STRVAR(StrokeVertexIterator_reversed_doc,
+".. method:: reversed()\n"
+"\n"
+" Returns a StrokeVertexIterator that traverses stroke vertices in the\n"
+" reversed order.\n"
+"\n"
+" :return: A StrokeVertexIterator traversing stroke vertices backward.\n"
+" :rtype: :class:`StrokeVertexIterator`");
+
+static PyObject *StrokeVertexIterator_reversed(BPy_StrokeVertexIterator *self)
+{
+ StrokeInternal::StrokeVertexIterator *copy = new StrokeInternal::StrokeVertexIterator(*self->sv_it);
+ return BPy_StrokeVertexIterator_from_StrokeVertexIterator(*copy, !self->reversed);
+}
+
+static PyMethodDef BPy_StrokeVertexIterator_methods[] = {
+ {"incremented", (PyCFunction) StrokeVertexIterator_incremented, METH_NOARGS, StrokeVertexIterator_incremented_doc},
+ {"decremented", (PyCFunction)StrokeVertexIterator_decremented, METH_NOARGS, StrokeVertexIterator_decremented_doc},
+ {"reversed", (PyCFunction)StrokeVertexIterator_reversed, METH_NOARGS, StrokeVertexIterator_reversed_doc},
+ {NULL, NULL, 0, NULL}
+};
+
/*----------------------StrokeVertexIterator get/setters ----------------------------*/
PyDoc_STRVAR(StrokeVertexIterator_object_doc,
@@ -130,7 +207,7 @@ PyDoc_STRVAR(StrokeVertexIterator_object_doc,
static PyObject *StrokeVertexIterator_object_get(BPy_StrokeVertexIterator *self, void *UNUSED(closure))
{
- if (!self->reversed && self->sv_it->isEnd()) {
+ if (self->sv_it->isEnd()) {
PyErr_SetString(PyExc_RuntimeError, "iteration has stopped");
return NULL;
}
@@ -198,7 +275,7 @@ PyTypeObject StrokeVertexIterator_Type = {
0, /* tp_weaklistoffset */
(getiterfunc)StrokeVertexIterator_iter, /* tp_iter */
(iternextfunc)StrokeVertexIterator_iternext, /* tp_iternext */
- 0, /* tp_methods */
+ BPy_StrokeVertexIterator_methods, /* tp_methods */
0, /* tp_members */
BPy_StrokeVertexIterator_getseters, /* tp_getset */
&Iterator_Type, /* tp_base */
diff --git a/source/blender/freestyle/intern/python/StrokeShader/BPy_CalligraphicShader.cpp b/source/blender/freestyle/intern/python/StrokeShader/BPy_CalligraphicShader.cpp
index 814d03b42d4..6b0c1424d61 100644
--- a/source/blender/freestyle/intern/python/StrokeShader/BPy_CalligraphicShader.cpp
+++ b/source/blender/freestyle/intern/python/StrokeShader/BPy_CalligraphicShader.cpp
@@ -67,11 +67,6 @@ static char CalligraphicShader___doc__[] =
" :arg stroke: A Stroke object.\n"
" :type stroke: :class:`Stroke`\n";
-static int convert_v2(PyObject *obj, void *v)
-{
- return float_array_from_PyObject(obj, (float *)v, 2);
-}
-
static int CalligraphicShader___init__(BPy_CalligraphicShader *self, PyObject *args, PyObject *kwds)
{
static const char *kwlist[] = {"thickness_min", "thickness_max", "orientation", "clamp", NULL};
diff --git a/source/blender/freestyle/intern/winged_edge/Curvature.cpp b/source/blender/freestyle/intern/winged_edge/Curvature.cpp
index 4a910099bff..38941b23357 100644
--- a/source/blender/freestyle/intern/winged_edge/Curvature.cpp
+++ b/source/blender/freestyle/intern/winged_edge/Curvature.cpp
@@ -539,7 +539,7 @@ inline static real angle(WOEdge *h)
{
const Vec3r& n1 = h->GetbFace()->GetNormal();
const Vec3r& n2 = h->GetaFace()->GetNormal();
- const Vec3r v = h->getVec3r();
+ const Vec3r v = h->GetVec();
real sine = (n1 ^ n2) * v / v.norm();
if (sine >= 1.0) {
return M_PI / 2.0;
diff --git a/source/blender/freestyle/intern/winged_edge/WEdge.cpp b/source/blender/freestyle/intern/winged_edge/WEdge.cpp
index 461df7bbf27..de166531d8b 100644
--- a/source/blender/freestyle/intern/winged_edge/WEdge.cpp
+++ b/source/blender/freestyle/intern/winged_edge/WEdge.cpp
@@ -211,11 +211,6 @@ WOEdge *WOEdge::duplicate()
return clone;
}
-Vec3r WOEdge::getVec3r ()
-{
- return Vec3r(_pbVertex->GetVertex() - _paVertex->GetVertex());
-}
-
WOEdge *WOEdge::twin ()
{
return GetOwner()->GetOtherOEdge(this);
diff --git a/source/blender/freestyle/intern/winged_edge/WEdge.h b/source/blender/freestyle/intern/winged_edge/WEdge.h
index 54461a6e8a6..6699a8113b6 100644
--- a/source/blender/freestyle/intern/winged_edge/WEdge.h
+++ b/source/blender/freestyle/intern/winged_edge/WEdge.h
@@ -525,8 +525,6 @@ public:
/*! Retrieves the list of edges in CW order */
inline void RetrieveCWOrderedEdges(vector<WEdge*>& oEdges);
- /*! returns the vector between the two vertices */
- Vec3r getVec3r ();
WOEdge *twin ();
WOEdge *getPrevOnFace();
diff --git a/source/blender/gpu/GPU_extensions.h b/source/blender/gpu/GPU_extensions.h
index e97cc57055f..04c4119df6e 100644
--- a/source/blender/gpu/GPU_extensions.h
+++ b/source/blender/gpu/GPU_extensions.h
@@ -117,6 +117,9 @@ GPUTexture *GPU_texture_create_vsm_shadow_map(int size, char err_out[256]);
GPUTexture *GPU_texture_from_blender(struct Image *ima,
struct ImageUser *iuser, bool is_data, double time, int mipmap);
GPUTexture *GPU_texture_from_preview(struct PreviewImage *prv, int mipmap);
+void GPU_invalid_tex_init(void);
+void GPU_invalid_tex_bind(int mode);
+void GPU_invalid_tex_free(void);
void GPU_texture_free(GPUTexture *tex);
diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.c
index 00f3823eee0..9f54e384f84 100644
--- a/source/blender/gpu/intern/gpu_extensions.c
+++ b/source/blender/gpu/intern/gpu_extensions.c
@@ -93,6 +93,9 @@ static struct GPUGlobal {
GPUOSType os;
GPUDriverType driver;
GPUShaders shaders;
+ GPUTexture *invalid_tex_1D; /* texture used in place of invalid textures (not loaded correctly, missing) */
+ GPUTexture *invalid_tex_2D;
+ GPUTexture *invalid_tex_3D;
} GG = {1, 0};
/* GPU Types */
@@ -222,6 +225,8 @@ void GPU_extensions_init(void)
GG.os = GPU_OS_UNIX;
#endif
+
+ GPU_invalid_tex_init();
GPU_simple_shaders_init();
}
@@ -230,6 +235,7 @@ void GPU_extensions_exit(void)
gpu_extensions_init = 0;
GPU_codegen_exit();
GPU_simple_shaders_exit();
+ GPU_invalid_tex_free();
}
int GPU_glsl_support(void)
@@ -571,11 +577,6 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, bool is_data,
return ima->gputexture;
}
- if (!bindcode) {
- glBindTexture(GL_TEXTURE_2D, lastbindcode);
- return NULL;
- }
-
tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture");
tex->bindcode = bindcode;
tex->number = -1;
@@ -586,7 +587,7 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, bool is_data,
ima->gputexture= tex;
if (!glIsTexture(tex->bindcode)) {
- GPU_print_error("Blender Texture");
+ GPU_print_error("Blender Texture Not Loaded");
}
else {
glBindTexture(GL_TEXTURE_2D, tex->bindcode);
@@ -624,12 +625,6 @@ GPUTexture *GPU_texture_from_preview(PreviewImage *prv, int mipmap)
return tex;
}
- /* error binding anything */
- if (!bindcode) {
- glBindTexture(GL_TEXTURE_2D, lastbindcode);
- return NULL;
- }
-
tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture");
tex->bindcode = bindcode;
tex->number = -1;
@@ -639,7 +634,7 @@ GPUTexture *GPU_texture_from_preview(PreviewImage *prv, int mipmap)
prv->gputexture[0]= tex;
if (!glIsTexture(tex->bindcode)) {
- GPU_print_error("Blender Texture");
+ GPU_print_error("Blender Texture Not Loaded");
}
else {
glBindTexture(GL_TEXTURE_2D, tex->bindcode);
@@ -705,6 +700,37 @@ GPUTexture *GPU_texture_create_vsm_shadow_map(int size, char err_out[256])
return tex;
}
+void GPU_invalid_tex_init(void)
+{
+ float color[4] = {1.0f, 0.0f, 1.0f, 1.0};
+ GG.invalid_tex_1D = GPU_texture_create_1D(1, color, NULL);
+ GG.invalid_tex_2D = GPU_texture_create_2D(1, 1, color, NULL);
+ GG.invalid_tex_3D = GPU_texture_create_3D(1, 1, 1, 4, color);
+}
+
+void GPU_invalid_tex_bind(int mode)
+{
+ switch (mode) {
+ case GL_TEXTURE_1D:
+ glBindTexture(GL_TEXTURE_1D, GG.invalid_tex_1D->bindcode);
+ break;
+ case GL_TEXTURE_2D:
+ glBindTexture(GL_TEXTURE_2D, GG.invalid_tex_2D->bindcode);
+ break;
+ case GL_TEXTURE_3D:
+ glBindTexture(GL_TEXTURE_3D, GG.invalid_tex_3D->bindcode);
+ break;
+ }
+}
+
+void GPU_invalid_tex_free(void)
+{
+ GPU_texture_free(GG.invalid_tex_1D);
+ GPU_texture_free(GG.invalid_tex_2D);
+ GPU_texture_free(GG.invalid_tex_3D);
+}
+
+
void GPU_texture_bind(GPUTexture *tex, int number)
{
GLenum arbnumber;
@@ -721,7 +747,11 @@ void GPU_texture_bind(GPUTexture *tex, int number)
arbnumber = (GLenum)((GLuint)GL_TEXTURE0_ARB + number);
if (number != 0) glActiveTextureARB(arbnumber);
- glBindTexture(tex->target, tex->bindcode);
+ if (tex->bindcode != 0) {
+ glBindTexture(tex->target, tex->bindcode);
+ }
+ else
+ GPU_invalid_tex_bind(tex->target);
glEnable(tex->target);
if (number != 0) glActiveTextureARB(GL_TEXTURE0_ARB);
@@ -1396,7 +1426,10 @@ void GPU_shader_uniform_texture(GPUShader *UNUSED(shader), int location, GPUText
arbnumber = (GLenum)((GLuint)GL_TEXTURE0_ARB + tex->number);
if (tex->number != 0) glActiveTextureARB(arbnumber);
- glBindTexture(tex->target, tex->bindcode);
+ if (tex->bindcode != 0)
+ glBindTexture(tex->target, tex->bindcode);
+ else
+ GPU_invalid_tex_bind(tex->target);
glUniform1iARB(location, tex->number);
glEnable(tex->target);
if (tex->number != 0) glActiveTextureARB(GL_TEXTURE0_ARB);
diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl
index 5c7872a8b3a..d5d0c7ef454 100644
--- a/source/blender/gpu/shaders/gpu_shader_material.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_material.glsl
@@ -752,6 +752,18 @@ void combine_rgb(float r, float g, float b, out vec4 col)
col = vec4(r, g, b, 1.0);
}
+void separate_xyz(vec3 vec, out float x, out float y, out float z)
+{
+ x = vec.r;
+ y = vec.g;
+ z = vec.b;
+}
+
+void combine_xyz(float x, float y, float z, out vec3 vec)
+{
+ vec = vec3(x, y, z);
+}
+
void separate_hsv(vec4 col, out float h, out float s, out float v)
{
vec4 hsv;
@@ -2169,7 +2181,7 @@ void node_subsurface_scattering(vec4 color, float scale, vec3 radius, float shar
node_bsdf_diffuse(color, 0.0, N, result);
}
-void node_bsdf_hair(vec4 color, float roughnessu, float roughnessv, out vec4 result)
+void node_bsdf_hair(vec4 color, float offset, float roughnessu, float roughnessv, out vec4 result)
{
result = color;
}
diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c
index 21ee8ebe237..696e34e58cb 100644
--- a/source/blender/imbuf/intern/colormanagement.c
+++ b/source/blender/imbuf/intern/colormanagement.c
@@ -1895,7 +1895,7 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, bool save_as_render, boo
* so much useful to just ignore alpha -- it leads to bad
* artifacts especially when saving byte images.
*
- * What we do here is we're overing our image on top of
+ * What we do here is we're overlaying our image on top of
* background color (which is currently black).
*
* This is quite much the same as what Gimp does and it
@@ -2809,7 +2809,7 @@ void IMB_partial_display_buffer_update(ImBuf *ibuf, const float *linear_buffer,
int y;
for (y = ymin; y < ymax; y++) {
int index = y * buffer_width * 4;
- memcpy(ibuf->rect + index, display_buffer + index, (xmax - xmin) * 4);
+ memcpy((unsigned char *)ibuf->rect + index, display_buffer + index, (xmax - xmin) * 4);
}
}
}
diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp
index 8d8bb938e98..2dca3114765 100644
--- a/source/blender/imbuf/intern/openexr/openexr_api.cpp
+++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp
@@ -1124,6 +1124,27 @@ static const char *exr_rgba_channelname(InputFile *file, const char *chan)
return chan;
}
+static bool exr_has_rgb(InputFile *file)
+{
+ return file->header().channels().findChannel("R") != NULL &&
+ file->header().channels().findChannel("G") != NULL &&
+ file->header().channels().findChannel("B") != NULL;
+}
+
+static bool exr_has_luma(InputFile *file)
+{
+ /* Y channel is the luma and should always present fir luma space images,
+ * optionally it could be also channels for chromas called BY and RY.
+ */
+ return file->header().channels().findChannel("Y") != NULL;
+}
+
+static bool exr_has_chroma(InputFile *file)
+{
+ return file->header().channels().findChannel("BY") != NULL &&
+ file->header().channels().findChannel("RY") != NULL;
+}
+
static int exr_has_zbuffer(InputFile *file)
{
return !(file->header().channels().findChannel("Z") == NULL);
@@ -1219,6 +1240,8 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char
}
}
else {
+ const bool has_rgb = exr_has_rgb(file);
+ const bool has_luma = exr_has_luma(file);
FrameBuffer frameBuffer;
float *first;
int xstride = sizeof(float) * 4;
@@ -1231,12 +1254,22 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char
/* but, since we read y-flipped (negative y stride) we move to last scanline */
first += 4 * (height - 1) * width;
- frameBuffer.insert(exr_rgba_channelname(file, "R"),
- Slice(Imf::FLOAT, (char *) first, xstride, ystride));
- frameBuffer.insert(exr_rgba_channelname(file, "G"),
- Slice(Imf::FLOAT, (char *) (first + 1), xstride, ystride));
- frameBuffer.insert(exr_rgba_channelname(file, "B"),
- Slice(Imf::FLOAT, (char *) (first + 2), xstride, ystride));
+ if (has_rgb) {
+ frameBuffer.insert(exr_rgba_channelname(file, "R"),
+ Slice(Imf::FLOAT, (char *) first, xstride, ystride));
+ frameBuffer.insert(exr_rgba_channelname(file, "G"),
+ Slice(Imf::FLOAT, (char *) (first + 1), xstride, ystride));
+ frameBuffer.insert(exr_rgba_channelname(file, "B"),
+ Slice(Imf::FLOAT, (char *) (first + 2), xstride, ystride));
+ }
+ else if (has_luma) {
+ frameBuffer.insert(exr_rgba_channelname(file, "Y"),
+ Slice(Imf::FLOAT, (char *) first, xstride, ystride));
+ frameBuffer.insert(exr_rgba_channelname(file, "BY"),
+ Slice(Imf::FLOAT, (char *) (first + 1), xstride, ystride, 1, 1, 0.5f));
+ frameBuffer.insert(exr_rgba_channelname(file, "RY"),
+ Slice(Imf::FLOAT, (char *) (first + 2), xstride, ystride, 1, 1, 0.5f));
+ }
/* 1.0 is fill value, this still needs to be assigned even when (is_alpha == 0) */
frameBuffer.insert(exr_rgba_channelname(file, "A"),
@@ -1264,6 +1297,24 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char
// if (flag & IM_rect)
// IMB_rect_from_float(ibuf);
+ if (!has_rgb && has_luma) {
+ size_t a;
+ if (exr_has_chroma(file)) {
+ for (a = 0; a < (size_t) ibuf->x * ibuf->y; ++a) {
+ float *color = ibuf->rect_float + a * 4;
+ ycc_to_rgb(color[0] * 255.0f, color[1] * 255.0f, color[2] * 255.0f,
+ &color[0], &color[1], &color[2],
+ BLI_YCC_ITU_BT709);
+ }
+ }
+ else {
+ for (a = 0; a < (size_t) ibuf->x * ibuf->y; ++a) {
+ float *color = ibuf->rect_float + a * 4;
+ color[1] = color[2] = color[0];
+ }
+ }
+ }
+
/* file is no longer needed */
delete file;
}
diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c
index 236d41078d6..9a97a142198 100644
--- a/source/blender/imbuf/intern/thumbs.c
+++ b/source/blender/imbuf/intern/thumbs.c
@@ -197,19 +197,6 @@ static void escape_uri_string(const char *string, char *escaped_string, int esca
*q = '\0';
}
-static void to_hex_char(char *hexbytes, const unsigned char *bytes, int len)
-{
- const unsigned char *p;
- char *q;
-
- for (q = hexbytes, p = bytes; len; p++) {
- const unsigned char c = (unsigned char) *p;
- len--;
- *q++ = hex[c >> 4];
- *q++ = hex[c & 15];
- }
-}
-
/** ----- end of adapted code from glib --- */
static int uri_from_filename(const char *path, char *uri)
@@ -258,9 +245,7 @@ static void thumbname_from_uri(const char *uri, char *thumb, const int thumb_len
md5_buffer(uri, strlen(uri), digest);
hexdigest[0] = '\0';
- to_hex_char(hexdigest, digest, 16);
- hexdigest[32] = '\0';
- BLI_snprintf(thumb, thumb_len, "%s.png", hexdigest);
+ BLI_snprintf(thumb, thumb_len, "%s.png", md5_to_hexdigest(digest, hexdigest));
// printf("%s: '%s' --> '%s'\n", __func__, uri, thumb);
}
diff --git a/source/blender/makesdna/DNA_actuator_types.h b/source/blender/makesdna/DNA_actuator_types.h
index 99f0c999a29..5ab799a75e5 100644
--- a/source/blender/makesdna/DNA_actuator_types.h
+++ b/source/blender/makesdna/DNA_actuator_types.h
@@ -245,6 +245,18 @@ typedef struct bSteeringActuator {
struct Object *navmesh;
} bSteeringActuator;
+typedef struct bMouseActuator {
+ short type; /* 0=Visibility, 1=Look */
+ short flag;
+
+ int object_axis[2];
+ float threshold[2];
+ float sensitivity[2];
+ float limit_x[2];
+ float limit_y[2];
+} bMouseActuator;
+
+
typedef struct bActuator {
struct bActuator *next, *prev, *mynew;
short type;
@@ -314,6 +326,7 @@ typedef struct bActuator {
#define ACT_STATE 22
#define ACT_ARMATURE 23
#define ACT_STEERING 24
+#define ACT_MOUSE 25
/* actuator flag */
#define ACT_SHOW 1
@@ -542,4 +555,22 @@ typedef struct bActuator {
#define ACT_STEERING_AUTOMATICFACING 4
#define ACT_STEERING_NORMALUP 8
+/* mouseactuator->type */
+#define ACT_MOUSE_VISIBILITY 0
+#define ACT_MOUSE_LOOK 1
+
+/* mouseactuator->flag */
+#define ACT_MOUSE_VISIBLE (1 << 0)
+#define ACT_MOUSE_USE_AXIS_X (1 << 1)
+#define ACT_MOUSE_USE_AXIS_Y (1 << 2)
+#define ACT_MOUSE_RESET_X (1 << 3)
+#define ACT_MOUSE_RESET_Y (1 << 4)
+#define ACT_MOUSE_LOCAL_X (1 << 5)
+#define ACT_MOUSE_LOCAL_Y (1 << 6)
+
+/* mouseactuator->object_axis */
+#define ACT_MOUSE_OBJECT_AXIS_X 0
+#define ACT_MOUSE_OBJECT_AXIS_Y 1
+#define ACT_MOUSE_OBJECT_AXIS_Z 2
+
#endif /* __DNA_ACTUATOR_TYPES_H__ */
diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h
index 0321c1cad12..d638d20c6a5 100644
--- a/source/blender/makesdna/DNA_curve_types.h
+++ b/source/blender/makesdna/DNA_curve_types.h
@@ -71,16 +71,6 @@ typedef struct Path {
/* These two Lines with # tell makesdna this struct can be excluded. */
#
#
-typedef struct BevList {
- struct BevList *next, *prev;
- int nr, dupe_nr;
- int poly, hole;
- int charidx;
-} BevList;
-
-/* These two Lines with # tell makesdna this struct can be excluded. */
-#
-#
typedef struct BevPoint {
float vec[3], alfa, radius, weight;
float sina, cosa; /* 2D Only */
@@ -88,6 +78,19 @@ typedef struct BevPoint {
short split_tag, dupe_tag;
} BevPoint;
+/* These two Lines with # tell makesdna this struct can be excluded. */
+#
+#
+typedef struct BevList {
+ struct BevList *next, *prev;
+ int nr, dupe_nr;
+ int poly, hole;
+ int charidx;
+
+ /* over-alloc */
+ BevPoint bevpoints[0];
+} BevList;
+
/**
* Keyframes on F-Curves (allows code reuse of Bezier eval code) and
* Points on Bezier Curves/Paths are generally BezTriples
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index e13d53934cb..b13353609c6 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -874,9 +874,10 @@ typedef struct NodeShaderUVMap {
#define CMP_NODE_CHANNEL_MATTE_CS_YCC 4
/* glossy distributions */
-#define SHD_GLOSSY_BECKMANN 0
-#define SHD_GLOSSY_SHARP 1
-#define SHD_GLOSSY_GGX 2
+#define SHD_GLOSSY_BECKMANN 0
+#define SHD_GLOSSY_SHARP 1
+#define SHD_GLOSSY_GGX 2
+#define SHD_GLOSSY_ASHIKHMIN_SHIRLEY 3
/* vector transform */
#define SHD_VECT_TRANSFORM_TYPE_VECTOR 0
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index ceb938c3af2..c95cdb5c1f8 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1442,8 +1442,9 @@ enum {
#define R_BAKE_LORES_MESH 32
#define R_BAKE_VCOL 64
#define R_BAKE_USERSCALE 128
-#define R_BAKE_SPLIT_MAT 256
-#define R_BAKE_AUTO_NAME 512
+#define R_BAKE_CAGE 256
+#define R_BAKE_SPLIT_MAT 512
+#define R_BAKE_AUTO_NAME 1024
/* bake_normal_space */
#define R_BAKE_SPACE_CAMERA 0
diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h
index 41d26e34f03..38d3c2786ce 100644
--- a/source/blender/makesdna/DNA_screen_types.h
+++ b/source/blender/makesdna/DNA_screen_types.h
@@ -383,6 +383,6 @@ enum {
#define RGN_DRAW 1
#define RGN_DRAW_PARTIAL 2
#define RGN_DRAWING 4
-
+#define RGN_DRAW_REFRESH_UI 8 /* re-create uiBlock's where possible */
#endif
diff --git a/source/blender/makesdna/DNA_sensor_types.h b/source/blender/makesdna/DNA_sensor_types.h
index fcdbbe31541..cd1977c0ce3 100644
--- a/source/blender/makesdna/DNA_sensor_types.h
+++ b/source/blender/makesdna/DNA_sensor_types.h
@@ -202,6 +202,8 @@ typedef struct bJoystickSensor {
#define SENS_PROP_INTERVAL 2
#define SENS_PROP_CHANGED 3
#define SENS_PROP_EXPRESSION 4
+#define SENS_PROP_LESSTHAN 5
+#define SENS_PROP_GREATERTHAN 6
/* raysensor->axisflag */
/* flip x and y to make y default!!! */
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 4f5670d16c1..987985f0ba7 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -521,6 +521,8 @@ typedef struct UserDef {
char author[80]; /* author name for file formats supporting it */
+ char font_path_ui[1024];
+
int compute_device_type;
int compute_device_id;
diff --git a/source/blender/makesrna/intern/rna_actuator.c b/source/blender/makesrna/intern/rna_actuator.c
index fd04fb46cf1..3cc530ec368 100644
--- a/source/blender/makesrna/intern/rna_actuator.c
+++ b/source/blender/makesrna/intern/rna_actuator.c
@@ -55,6 +55,7 @@ static EnumPropertyItem actuator_type_items[] = {
{ACT_2DFILTER, "FILTER_2D", 0, "Filter 2D", ""},
{ACT_GAME, "GAME", 0, "Game", ""},
{ACT_MESSAGE, "MESSAGE", 0, "Message", ""},
+ {ACT_MOUSE, "MOUSE", 0, "Mouse", ""},
{ACT_OBJECT, "MOTION", 0, "Motion", ""},
{ACT_PARENT, "PARENT", 0, "Parent", ""},
{ACT_PROPERTY, "PROPERTY", 0, "Property", ""},
@@ -110,6 +111,8 @@ static StructRNA *rna_Actuator_refine(struct PointerRNA *ptr)
return &RNA_ArmatureActuator;
case ACT_STEERING:
return &RNA_SteeringActuator;
+ case ACT_MOUSE:
+ return &RNA_MouseActuator;
default:
return &RNA_Actuator;
}
@@ -459,6 +462,7 @@ EnumPropertyItem *rna_Actuator_type_itemf(bContext *C, PointerRNA *ptr, Property
RNA_enum_items_add_value(&item, &totitem, actuator_type_items, ACT_2DFILTER);
RNA_enum_items_add_value(&item, &totitem, actuator_type_items, ACT_GAME);
RNA_enum_items_add_value(&item, &totitem, actuator_type_items, ACT_MESSAGE);
+ RNA_enum_items_add_value(&item, &totitem, actuator_type_items, ACT_MOUSE);
RNA_enum_items_add_value(&item, &totitem, actuator_type_items, ACT_OBJECT);
RNA_enum_items_add_value(&item, &totitem, actuator_type_items, ACT_PARENT);
RNA_enum_items_add_value(&item, &totitem, actuator_type_items, ACT_PROPERTY);
@@ -2038,6 +2042,132 @@ static void rna_def_steering_actuator(BlenderRNA *brna)
RNA_def_property_update(prop, NC_LOGIC, NULL);
}
+static void rna_def_mouse_actuator(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem prop_type_items[] = {
+ {ACT_MOUSE_VISIBILITY, "VISIBILITY", 0, "Visibility", ""},
+ {ACT_MOUSE_LOOK, "LOOK", 0, "Look", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem prop_object_axis_items[] = {
+ {ACT_MOUSE_OBJECT_AXIS_X, "OBJECT_AXIS_X", 0, "X Axis", ""},
+ {ACT_MOUSE_OBJECT_AXIS_Y, "OBJECT_AXIS_Y", 0, "Y Axis", ""},
+ {ACT_MOUSE_OBJECT_AXIS_Z, "OBJECT_AXIS_Z", 0, "Z Axis", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ srna = RNA_def_struct(brna, "MouseActuator", "Actuator");
+ RNA_def_struct_ui_text(srna, "Mouse Actuator", "Actuator to ..");
+ RNA_def_struct_sdna_from(srna, "bMouseActuator", "data");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "type");
+ RNA_def_property_enum_items(prop, prop_type_items);
+ RNA_def_property_ui_text(prop, "Mode", "");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ /* Visibility */
+ prop = RNA_def_property(srna, "visible", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", ACT_MOUSE_VISIBLE);
+ RNA_def_property_ui_text(prop, "Visible", "Make mouse cursor visible");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ /* Mouse Look */
+ prop = RNA_def_property(srna, "use_axis_x", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", ACT_MOUSE_USE_AXIS_X);
+ RNA_def_property_ui_text(prop, "Use X Axis", "Calculate mouse movement on the X axis");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ prop = RNA_def_property(srna, "use_axis_y", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", ACT_MOUSE_USE_AXIS_Y);
+ RNA_def_property_ui_text(prop, "Use Y Axis", "Calculate mouse movement on the Y axis");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ prop = RNA_def_property(srna, "reset_x", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", ACT_MOUSE_RESET_X);
+ RNA_def_property_ui_text(prop, "Reset", "Reset the cursor's X position to the center of the screen space after calculating");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ prop = RNA_def_property(srna, "reset_y", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", ACT_MOUSE_RESET_Y);
+ RNA_def_property_ui_text(prop, "Reset", "Reset the cursor's Y position to the center of the screen space after calculating");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ prop = RNA_def_property(srna, "local_x", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", ACT_MOUSE_LOCAL_X);
+ RNA_def_property_ui_text(prop, "Local", "Apply rotation locally");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ prop = RNA_def_property(srna, "local_y", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", ACT_MOUSE_LOCAL_Y);
+ RNA_def_property_ui_text(prop, "Local", "Apply rotation locally");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ prop = RNA_def_property(srna, "threshold_x", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "threshold[0]");
+ RNA_def_property_ui_range(prop, 0, 0.5, 1, 3);
+ RNA_def_property_ui_text(prop, "Threshold", "The amount of X motion before mouse movement will register");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ prop = RNA_def_property(srna, "threshold_y", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "threshold[1]");
+ RNA_def_property_ui_range(prop, 0, 0.5, 1, 3);
+ RNA_def_property_ui_text(prop, "Threshold", "The amount of Y motion before mouse movement will register");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ prop = RNA_def_property(srna, "object_axis_x", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "object_axis[0]");
+ RNA_def_property_enum_items(prop, prop_object_axis_items);
+ RNA_def_property_ui_text(prop, "Obj Axis", "Local object axis mouse movement in the X direction will apply to");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ prop = RNA_def_property(srna, "object_axis_y", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "object_axis[1]");
+ RNA_def_property_enum_items(prop, prop_object_axis_items);
+ RNA_def_property_ui_text(prop, "Obj Axis", "The object axis mouse movement in the Y direction will apply to");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ prop = RNA_def_property(srna, "sensitivity_x", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "sensitivity[0]");
+ RNA_def_property_ui_range(prop, -100.0, 100.0, 0.2, 3);
+ RNA_def_property_ui_text(prop, "Sensitivity", "Set the sensitivity of the X axis");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ prop = RNA_def_property(srna, "sensitivity_y", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "sensitivity[1]");
+ RNA_def_property_ui_range(prop, -100.0, 100.0, 0.2, 3);
+ RNA_def_property_ui_text(prop, "Sensitivity", "Set the sensitivity of the Y axis");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ prop = RNA_def_property(srna, "min_x", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_float_sdna(prop, NULL, "limit_x[0]");
+ RNA_def_property_ui_range(prop, DEG2RADF(-3600.0f), 0.0, 9, 3);
+ RNA_def_property_ui_text(prop, "min", "The maximum negative rotation allowed by x mouse movement (0 for infinite)");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ prop = RNA_def_property(srna, "max_x", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_float_sdna(prop, NULL, "limit_x[1]");
+ RNA_def_property_ui_range(prop, 0.0, DEG2RADF(3600.0f), 9, 3);
+ RNA_def_property_ui_text(prop, "max", "The maximum positive rotation allowed by x mouse movement (0 for infinite)");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ prop = RNA_def_property(srna, "min_y", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_float_sdna(prop, NULL, "limit_y[0]");
+ RNA_def_property_ui_range(prop, DEG2RADF(-3600.0f), 0.0, 9, 3);
+ RNA_def_property_ui_text(prop, "min", "The maximum negative rotation allowed by y mouse movement (0 for infinite)");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+
+ prop = RNA_def_property(srna, "max_y", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_float_sdna(prop, NULL, "limit_y[1]");
+ RNA_def_property_ui_range(prop, 0.0, DEG2RADF(3600.0f), 9, 3);
+ RNA_def_property_ui_text(prop, "max", "The maximum positive rotation allowed by y mouse movement (0 for infinite)");
+ RNA_def_property_update(prop, NC_LOGIC, NULL);
+}
+
void RNA_def_actuator(BlenderRNA *brna)
{
rna_def_actuator(brna);
@@ -2059,6 +2189,7 @@ void RNA_def_actuator(BlenderRNA *brna)
rna_def_state_actuator(brna);
rna_def_armature_actuator(brna);
rna_def_steering_actuator(brna);
+ rna_def_mouse_actuator(brna);
}
#endif
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index 191d6d230db..7ffc138e1a3 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -1279,6 +1279,11 @@ static void rna_def_modifier_hook(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Force", "Relative force of the hook");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+ prop = RNA_def_property(srna, "center", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "cent");
+ RNA_def_property_ui_text(prop, "Hook Center", "");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Object", "Parent Object for hook, also recalculates and clears offset");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index c39d3826d8b..e550c4e85fb 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -2946,9 +2946,24 @@ static EnumPropertyItem node_ycc_items[] = {
};
static EnumPropertyItem node_glossy_items[] = {
- {SHD_GLOSSY_SHARP, "SHARP", 0, "Sharp", ""},
- {SHD_GLOSSY_BECKMANN, "BECKMANN", 0, "Beckmann", ""},
- {SHD_GLOSSY_GGX, "GGX", 0, "GGX", ""},
+ {SHD_GLOSSY_SHARP, "SHARP", 0, "Sharp", ""},
+ {SHD_GLOSSY_BECKMANN, "BECKMANN", 0, "Beckmann", ""},
+ {SHD_GLOSSY_GGX, "GGX", 0, "GGX", ""},
+ {SHD_GLOSSY_ASHIKHMIN_SHIRLEY, "ASHIKHMIN_SHIRLEY", 0, "Ashikhmin-Shirley", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+
+static EnumPropertyItem node_anisotropic_items[] = {
+ {SHD_GLOSSY_BECKMANN, "BECKMANN", 0, "Beckmann", ""},
+ {SHD_GLOSSY_GGX, "GGX", 0, "GGX", ""},
+ {SHD_GLOSSY_ASHIKHMIN_SHIRLEY, "ASHIKHMIN_SHIRLEY", 0, "Ashikhmin-Shirley", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+
+static EnumPropertyItem node_glass_items[] = {
+ {SHD_GLOSSY_SHARP, "SHARP", 0, "Sharp", ""},
+ {SHD_GLOSSY_BECKMANN, "BECKMANN", 0, "Beckmann", ""},
+ {SHD_GLOSSY_GGX, "GGX", 0, "GGX", ""},
{0, NULL, 0, NULL, NULL}
};
@@ -3699,6 +3714,28 @@ static void def_glossy(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_glass(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "distribution", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, node_glass_items);
+ RNA_def_property_ui_text(prop, "Distribution", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
+static void def_anisotropic(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "distribution", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, node_anisotropic_items);
+ RNA_def_property_ui_text(prop, "Distribution", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
static void def_toon(StructRNA *srna)
{
PropertyRNA *prop;
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 82cea3d20e3..bcdb469dd85 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -2188,7 +2188,7 @@ static void rna_def_statvis(BlenderRNA *brna)
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, stat_type);
- RNA_def_property_ui_text(prop, "Type", "XXX");
+ RNA_def_property_ui_text(prop, "Type", "Type of data to visualize/check");
RNA_def_property_update(prop, 0, "rna_EditMesh_update");
@@ -3218,8 +3218,10 @@ static void rna_def_bake_data(BlenderRNA *brna)
RNA_def_struct_nested(brna, srna, "RenderSettings");
RNA_def_struct_ui_text(srna, "Bake Data", "Bake data for a Scene datablock");
- prop = RNA_def_property(srna, "cage", PROP_STRING, PROP_NONE);
- RNA_def_property_ui_text(prop, "Cage", "Object to use as cage");
+ prop = RNA_def_property(srna, "cage_object", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "cage");
+ RNA_def_property_ui_text(prop, "Cage Object", "Object to use as cage "
+ "instead of calculating the cage from the active object with cage extrusion");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
@@ -3309,6 +3311,12 @@ static void rna_def_bake_data(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Automatic Name",
"Automatically name the output file with the pass type (external only)");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+ prop = RNA_def_property(srna, "use_cage", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", R_BAKE_CAGE);
+ RNA_def_property_ui_text(prop, "Cage",
+ "Cast rays to active object from a cage");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
}
static void rna_def_scene_game_data(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_sensor.c b/source/blender/makesrna/intern/rna_sensor.c
index f5e59119baa..aeef04f4ac7 100644
--- a/source/blender/makesrna/intern/rna_sensor.c
+++ b/source/blender/makesrna/intern/rna_sensor.c
@@ -478,6 +478,8 @@ static void rna_def_property_sensor(BlenderRNA *brna)
{SENS_PROP_INTERVAL, "PROPINTERVAL", 0, "Interval", ""},
{SENS_PROP_CHANGED, "PROPCHANGED", 0, "Changed", ""},
/* {SENS_PROP_EXPRESSION, "PROPEXPRESSION", 0, "Expression", ""}, NOT_USED_IN_UI */
+ {SENS_PROP_LESSTHAN, "PROPLESSTHAN", 0, "Less Than", ""},
+ {SENS_PROP_GREATERTHAN, "PROPGREATERTHAN", 0, "Greater Than", ""},
{0, NULL, 0, NULL, NULL}
};
@@ -498,7 +500,7 @@ static void rna_def_property_sensor(BlenderRNA *brna)
prop = RNA_def_property(srna, "value", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "value");
- RNA_def_property_ui_text(prop, "Value", "Check for this value in types in Equal or Not Equal types");
+ RNA_def_property_ui_text(prop, "Value", "Check for this value in types in Equal, Not Equal, Less Than and Greater Than types");
RNA_def_property_update(prop, NC_LOGIC, NULL);
prop = RNA_def_property(srna, "value_min", PROP_STRING, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index d478bf41287..ddae8b2a01a 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -299,6 +299,14 @@ static void rna_Sequence_channel_set(PointerRNA *ptr, int value)
BKE_sequencer_sort(scene);
}
+static void rna_Sequence_frame_offset_range(PointerRNA *ptr, int *min, int *max,
+ int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ Sequence *seq = (Sequence *)ptr->data;
+ *min = ELEM(seq->type, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SOUND_HD) ? 0 : INT_MIN;
+ *max = INT_MAX;
+}
+
static void rna_Sequence_use_proxy_set(PointerRNA *ptr, int value)
{
Sequence *seq = (Sequence *)ptr->data;
@@ -1466,12 +1474,14 @@ static void rna_def_sequence(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "startofs");
// RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* overlap tests */
RNA_def_property_ui_text(prop, "Start Offset", "");
+ RNA_def_property_int_funcs(prop, NULL, NULL, "rna_Sequence_frame_offset_range");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_frame_change_update");
prop = RNA_def_property(srna, "frame_offset_end", PROP_INT, PROP_TIME);
RNA_def_property_int_sdna(prop, NULL, "endofs");
// RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* overlap tests */
RNA_def_property_ui_text(prop, "End Offset", "");
+ RNA_def_property_int_funcs(prop, NULL, NULL, "rna_Sequence_frame_offset_range");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_frame_change_update");
prop = RNA_def_property(srna, "frame_still_start", PROP_INT, PROP_TIME);
diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c
index b3a37a4ce8e..30fcb63169b 100644
--- a/source/blender/makesrna/intern/rna_sequencer_api.c
+++ b/source/blender/makesrna/intern/rna_sequencer_api.c
@@ -402,7 +402,7 @@ void RNA_api_sequence_strip(StructRNA *srna)
func = RNA_def_function(srna, "update", "rna_Sequence_update_rnafunc");
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
RNA_def_function_ui_description(func, "Update the strip dimensions");
- parm = RNA_def_boolean(func, "data", false, "Frame",
+ parm = RNA_def_boolean(func, "data", false, "Data",
"Update strip data");
func = RNA_def_function(srna, "strip_elem_from_frame", "BKE_sequencer_give_stripelem");
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 7c101fb19be..f69e768670e 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -386,7 +386,7 @@ static void rna_userdef_pathcompare_remove(ReportList *reports, PointerRNA *path
static void rna_userdef_temp_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr))
{
- BLI_init_temporary_dir(U.tempdir);
+ BLI_temp_dir_init(U.tempdir);
}
static void rna_userdef_text_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr))
@@ -3707,7 +3707,12 @@ static void rna_def_userdef_system(BlenderRNA *brna)
RNA_def_property_range(prop, 48, 144);
RNA_def_property_ui_text(prop, "DPI", "Font size and resolution for display");
RNA_def_property_update(prop, 0, "rna_userdef_dpi_update");
-
+
+ prop = RNA_def_property(srna, "font_path_ui", PROP_STRING, PROP_FILEPATH);
+ RNA_def_property_string_sdna(prop, NULL, "font_path_ui");
+ RNA_def_property_ui_text(prop, "Interface Font", "Path to interface font");
+ RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_language_update");
+
prop = RNA_def_property(srna, "scrollback", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "scrollback");
RNA_def_property_range(prop, 32, 32768);
diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c
index 127972a2e40..d3c7e7d4779 100644
--- a/source/blender/modifiers/intern/MOD_explode.c
+++ b/source/blender/modifiers/intern/MOD_explode.c
@@ -217,7 +217,7 @@ static MFace *get_dface(DerivedMesh *dm, DerivedMesh *split, int cur, int i, MFa
} (void)0
#define GET_ES(v1, v2) edgecut_get(eh, v1, v2)
-#define INT_UV(uvf, c0, c1) interp_v2_v2v2(uvf, mf->uv[c0], mf->uv[c1], 0.5f)
+#define INT_UV(uvf, c0, c1) mid_v2_v2v2(uvf, mf->uv[c0], mf->uv[c1])
static void remap_faces_3_6_9_12(DerivedMesh *dm, DerivedMesh *split, MFace *mf, int *facepa, int *vertpa, int i, EdgeHash *eh, int cur, int v1, int v2, int v3, int v4)
{
diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c
index d3bd05baa6d..f613d6063fe 100644
--- a/source/blender/modifiers/intern/MOD_laplaciandeform.c
+++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c
@@ -28,8 +28,9 @@
* \ingroup modifiers
*/
-#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BLI_stackdefines.h"
+#include "BLI_math.h"
#include "BLI_string.h"
#include "MEM_guardedalloc.h"
@@ -623,7 +624,6 @@ static void initSystem(LaplacianDeformModifierData *lmd, Object *ob, DerivedMesh
memcpy(sys->index_anchors, index_anchors, sizeof(int) * total_anchors);
memcpy(sys->co, vertexCos, sizeof(float[3]) * numVerts);
MEM_freeN(index_anchors);
- STACK_FREE(index_anchors);
lmd->vertexco = MEM_mallocN(sizeof(float[3]) * numVerts, "ModDeformCoordinates");
memcpy(lmd->vertexco, vertexCos, sizeof(float[3]) * numVerts);
lmd->total_verts = numVerts;
diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c
index c95cd96757f..deae10b5bcb 100644
--- a/source/blender/modifiers/intern/MOD_multires.c
+++ b/source/blender/modifiers/intern/MOD_multires.c
@@ -74,7 +74,8 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *dm,
MultiresModifierData *mmd = (MultiresModifierData *)md;
DerivedMesh *result;
Mesh *me = (Mesh *)ob->data;
- const int useRenderParams = flag & MOD_APPLY_RENDER;
+ const bool useRenderParams = (flag & MOD_APPLY_RENDER) != 0;
+ const bool ignore_simplify = (flag & MOD_APPLY_IGNORE_SIMPLIFY) != 0;
MultiresFlags flags = 0;
const bool has_mask = CustomData_has_layer(&me->ldata, CD_GRID_PAINT_MASK);
@@ -91,6 +92,9 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *dm,
if (useRenderParams)
flags |= MULTIRES_USE_RENDER_PARAMS;
+ if (ignore_simplify)
+ flags |= MULTIRES_IGNORE_SIMPLIFY;
+
result = multires_make_derived_from_derived(dm, mmd, ob, flags);
if (result == dm)
diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c
index 019eb54a9ee..832565cc247 100644
--- a/source/blender/modifiers/intern/MOD_skin.c
+++ b/source/blender/modifiers/intern/MOD_skin.c
@@ -68,6 +68,7 @@
#include "BLI_heap.h"
#include "BLI_math.h"
#include "BLI_stack.h"
+#include "BLI_bitmap.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_deform.h"
@@ -641,7 +642,7 @@ typedef struct {
int e;
} EdgeStackElem;
-static void build_emats_stack(BLI_Stack *stack, int *visited_e, EMat *emat,
+static void build_emats_stack(BLI_Stack *stack, BLI_bitmap *visited_e, EMat *emat,
const MeshElemMap *emap, const MEdge *medge,
const MVertSkin *vs, const MVert *mvert)
{
@@ -654,11 +655,11 @@ static void build_emats_stack(BLI_Stack *stack, int *visited_e, EMat *emat,
e = stack_elem.e;
/* Skip if edge already visited */
- if (visited_e[e])
+ if (BLI_BITMAP_TEST(visited_e, e))
return;
/* Mark edge as visited */
- visited_e[e] = true;
+ BLI_BITMAP_ENABLE(visited_e, e);
/* Process edge */
@@ -704,11 +705,12 @@ static EMat *build_edge_mats(const MVertSkin *vs,
BLI_Stack *stack;
EMat *emat;
EdgeStackElem stack_elem;
- int *visited_e, i, v;
+ BLI_bitmap *visited_e;
+ int i, v;
stack = BLI_stack_new(sizeof(stack_elem), "build_edge_mats.stack");
- visited_e = MEM_callocN(sizeof(int) * totedge, "build_edge_mats.visited_e");
+ visited_e = BLI_BITMAP_NEW(totedge, "build_edge_mats.visited_e");
emat = MEM_callocN(sizeof(EMat) * totedge, "build_edge_mats.emat");
/* Edge matrices are built from the root nodes, add all roots with
@@ -1703,6 +1705,11 @@ static BMesh *build_skin(SkinNode *skin_nodes,
so.bm = BM_mesh_create(&bm_mesh_allocsize_default);
so.mat_nr = 0;
+ /* BMESH_TODO: bumping up the stack level (see MOD_array.c) */
+ BM_mesh_elem_toolflags_ensure(so.bm);
+ BMO_push(so.bm, NULL);
+ bmesh_edit_begin(so.bm, 0);
+
if (input_dvert)
BM_data_layer_add(so.bm, &so.bm->vdata, CD_MDEFORMVERT);
@@ -1750,13 +1757,12 @@ static BMesh *build_skin(SkinNode *skin_nodes,
static void skin_set_orig_indices(DerivedMesh *dm)
{
- int *orig, totpoly, i;
+ int *orig, totpoly;
totpoly = dm->getNumPolys(dm);
orig = CustomData_add_layer(&dm->polyData, CD_ORIGINDEX,
CD_CALLOC, NULL, totpoly);
- for (i = 0; i < totpoly; i++)
- orig[i] = ORIGINDEX_NONE;
+ fill_vn_i(orig, totpoly, ORIGINDEX_NONE);
}
/*
diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c
index c212c13e396..14a6b631bd6 100644
--- a/source/blender/modifiers/intern/MOD_solidify.c
+++ b/source/blender/modifiers/intern/MOD_solidify.c
@@ -35,6 +35,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
+#include "BLI_stackdefines.h"
#include "BLI_bitmap.h"
#include "BLI_math.h"
@@ -325,8 +326,8 @@ static DerivedMesh *applyModifier(
for (eidx = 0, ed = orig_medge; eidx < numEdges; eidx++, ed++) {
if (!ELEM(edge_users[eidx], INVALID_UNUSED, INVALID_PAIR)) {
- BLI_BITMAP_SET(orig_mvert_tag, ed->v1);
- BLI_BITMAP_SET(orig_mvert_tag, ed->v2);
+ BLI_BITMAP_ENABLE(orig_mvert_tag, ed->v1);
+ BLI_BITMAP_ENABLE(orig_mvert_tag, ed->v2);
STACK_PUSH(new_edge_arr, eidx);
newFaces++;
newLoops += 4;
@@ -337,7 +338,7 @@ static DerivedMesh *applyModifier(
#undef INVALID_PAIR
for (i = 0; i < numVerts; i++) {
- if (BLI_BITMAP_GET(orig_mvert_tag, i)) {
+ if (BLI_BITMAP_TEST(orig_mvert_tag, i)) {
old_vert_arr[i] = STACK_SIZE(new_vert_arr);
STACK_PUSH(new_vert_arr, i);
newEdges++;
@@ -813,9 +814,6 @@ static DerivedMesh *applyModifier(
MEM_freeN(edge_order);
}
- STACK_FREE(new_vert_arr);
- STACK_FREE(new_edge_arr);
-
if (old_vert_arr)
MEM_freeN(old_vert_arr);
diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c
index 3de85047ebf..f17858264f8 100644
--- a/source/blender/modifiers/intern/MOD_subsurf.c
+++ b/source/blender/modifiers/intern/MOD_subsurf.c
@@ -98,8 +98,8 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
SubsurfModifierData *smd = (SubsurfModifierData *) md;
SubsurfFlags subsurf_flags = 0;
DerivedMesh *result;
- const int useRenderParams = flag & MOD_APPLY_RENDER;
- const int isFinalCalc = flag & MOD_APPLY_USECACHE;
+ const bool useRenderParams = (flag & MOD_APPLY_RENDER) != 0;
+ const bool isFinalCalc = (flag & MOD_APPLY_USECACHE) != 0;
if (useRenderParams)
subsurf_flags |= SUBSURF_USE_RENDER_PARAMS;
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index b4643813108..3899b3dce30 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -145,6 +145,7 @@ set(SRC
shader/nodes/node_shader_rgb.c
shader/nodes/node_shader_sepcombRGB.c
shader/nodes/node_shader_sepcombHSV.c
+ shader/nodes/node_shader_sepcombXYZ.c
shader/nodes/node_shader_squeeze.c
shader/nodes/node_shader_texture.c
shader/nodes/node_shader_valToRgb.c
diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h
index 91b103da0e2..255ad568bad 100644
--- a/source/blender/nodes/NOD_shader.h
+++ b/source/blender/nodes/NOD_shader.h
@@ -71,6 +71,8 @@ void register_node_type_sh_seprgb(void);
void register_node_type_sh_combrgb(void);
void register_node_type_sh_sephsv(void);
void register_node_type_sh_combhsv(void);
+void register_node_type_sh_sepxyz(void);
+void register_node_type_sh_combxyz(void);
void register_node_type_sh_hue_sat(void);
void register_node_type_sh_tex_brick(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 434e737772d..8d6c4abaef6 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -77,11 +77,11 @@ DefNode( ShaderNode, SH_NODE_ATTRIBUTE, def_sh_attribute, "AT
DefNode( ShaderNode, SH_NODE_AMBIENT_OCCLUSION, 0, "AMBIENT_OCCLUSION", AmbientOcclusion, "Ambient Occlusion", "" )
DefNode( ShaderNode, SH_NODE_BACKGROUND, 0, "BACKGROUND", Background, "Background", "" )
DefNode( ShaderNode, SH_NODE_HOLDOUT, 0, "HOLDOUT", Holdout, "Holdout", "" )
-DefNode( ShaderNode, SH_NODE_BSDF_ANISOTROPIC, 0, "BSDF_ANISOTROPIC", BsdfAnisotropic, "Anisotropic BSDF", "" )
+DefNode( ShaderNode, SH_NODE_BSDF_ANISOTROPIC, def_anisotropic, "BSDF_ANISOTROPIC", BsdfAnisotropic, "Anisotropic BSDF", "" )
DefNode( ShaderNode, SH_NODE_BSDF_DIFFUSE, 0, "BSDF_DIFFUSE", BsdfDiffuse, "Diffuse BSDF", "" )
DefNode( ShaderNode, SH_NODE_BSDF_GLOSSY, def_glossy, "BSDF_GLOSSY", BsdfGlossy, "Glossy BSDF", "" )
-DefNode( ShaderNode, SH_NODE_BSDF_GLASS, def_glossy, "BSDF_GLASS", BsdfGlass, "Glass BSDF", "" )
-DefNode( ShaderNode, SH_NODE_BSDF_REFRACTION, def_glossy, "BSDF_REFRACTION", BsdfRefraction, "Refraction BSDF", "" )
+DefNode( ShaderNode, SH_NODE_BSDF_GLASS, def_glass, "BSDF_GLASS", BsdfGlass, "Glass BSDF", "" )
+DefNode( ShaderNode, SH_NODE_BSDF_REFRACTION, def_glass, "BSDF_REFRACTION", BsdfRefraction, "Refraction BSDF", "" )
DefNode( ShaderNode, SH_NODE_BSDF_TRANSLUCENT, 0, "BSDF_TRANSLUCENT", BsdfTranslucent, "Translucent BSDF", "" )
DefNode( ShaderNode, SH_NODE_BSDF_TRANSPARENT, 0, "BSDF_TRANSPARENT", BsdfTransparent, "Transparent BSDF", "" )
DefNode( ShaderNode, SH_NODE_BSDF_VELVET, 0, "BSDF_VELVET", BsdfVelvet, "Velvet BSDF", "" )
@@ -120,6 +120,8 @@ DefNode( ShaderNode, SH_NODE_VECT_TRANSFORM, def_sh_vect_transform, "VE
DefNode( ShaderNode, SH_NODE_SEPHSV, 0, "SEPHSV", SeparateHSV, "Separate HSV", "" )
DefNode( ShaderNode, SH_NODE_COMBHSV, 0, "COMBHSV", CombineHSV, "Combine HSV", "" )
DefNode( ShaderNode, SH_NODE_UVMAP, def_sh_uvmap, "UVMAP", UVMap, "UV Map", "" )
+DefNode( ShaderNode, SH_NODE_SEPXYZ, 0, "SEPXYZ", SeparateXYZ, "Separate XYZ", "" )
+DefNode( ShaderNode, SH_NODE_COMBXYZ, 0, "COMBXYZ", CombineXYZ, "Combine XYZ", "" )
DefNode( CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" )
DefNode( CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" )
diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c
index 53f97e0d36c..4288b8fbcdc 100644
--- a/source/blender/nodes/shader/node_shader_tree.c
+++ b/source/blender/nodes/shader/node_shader_tree.c
@@ -79,7 +79,9 @@ static void shader_get_from_context(const bContext *C, bNodeTreeType *UNUSED(tre
Scene *scene = CTX_data_scene(C);
Object *ob = OBACT;
- if (!BKE_scene_use_new_shading_nodes(scene) || snode->shaderfrom == SNODE_SHADER_OBJECT) {
+ if ((snode->shaderfrom == SNODE_SHADER_OBJECT) ||
+ (BKE_scene_use_new_shading_nodes(scene) == false))
+ {
if (ob) {
*r_from = &ob->id;
if (ob->type == OB_LAMP) {
diff --git a/source/blender/nodes/shader/node_shader_util.c b/source/blender/nodes/shader/node_shader_util.c
index b00d96de935..49881381253 100644
--- a/source/blender/nodes/shader/node_shader_util.c
+++ b/source/blender/nodes/shader/node_shader_util.c
@@ -195,24 +195,48 @@ static void data_from_gpu_stack_list(ListBase *sockets, bNodeStack **ns, GPUNode
bNode *nodeGetActiveTexture(bNodeTree *ntree)
{
/* this is the node we texture paint and draw in textured draw */
- bNode *node, *tnode, *inactivenode = NULL;
+ bNode *node, *tnode, *inactivenode = NULL, *activetexnode = NULL, *activegroup = NULL;
+ bool hasgroup = false;
if (!ntree)
return NULL;
for (node = ntree->nodes.first; node; node = node->next) {
- if (node->flag & NODE_ACTIVE_TEXTURE)
- return node;
+ if (node->flag & NODE_ACTIVE_TEXTURE) {
+ activetexnode = node;
+ /* if active we can return immediately */
+ if (node->flag & NODE_ACTIVE)
+ return node;
+ }
else if (!inactivenode && node->typeinfo->nclass == NODE_CLASS_TEXTURE)
inactivenode = node;
+ else if (node->type == NODE_GROUP) {
+ if (node->flag & NODE_ACTIVE)
+ activegroup = node;
+ else
+ hasgroup = true;
+ }
+ }
+
+ /* first, check active group for textures */
+ if (activegroup) {
+ tnode = nodeGetActiveTexture((bNodeTree *)activegroup->id);
+ /* active node takes priority, so ignore any other possible nodes here */
+ if (tnode)
+ return tnode;
}
+
+ if (activetexnode)
+ return activetexnode;
- /* node active texture node in this tree, look inside groups */
- for (node = ntree->nodes.first; node; node = node->next) {
- if (node->type == NODE_GROUP) {
- tnode = nodeGetActiveTexture((bNodeTree *)node->id);
- if (tnode && ((tnode->flag & NODE_ACTIVE_TEXTURE) || !inactivenode))
- return tnode;
+ if (hasgroup) {
+ /* node active texture node in this tree, look inside groups */
+ for (node = ntree->nodes.first; node; node = node->next) {
+ if (node->type == NODE_GROUP) {
+ tnode = nodeGetActiveTexture((bNodeTree *)node->id);
+ if (tnode && ((tnode->flag & NODE_ACTIVE_TEXTURE) || !inactivenode))
+ return tnode;
+ }
}
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.c
index dd14006cd8a..b9c94fc3aee 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.c
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.c
@@ -44,6 +44,11 @@ static bNodeSocketTemplate sh_node_bsdf_anisotropic_out[] = {
{ -1, 0, "" }
};
+static void node_shader_init_anisotropic(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ node->custom1 = SHD_GLOSSY_GGX;
+}
+
static int node_shader_gpu_bsdf_anisotropic(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
{
if (!in[3].link)
@@ -61,7 +66,7 @@ void register_node_type_sh_bsdf_anisotropic(void)
node_type_compatibility(&ntype, NODE_NEW_SHADING);
node_type_socket_templates(&ntype, sh_node_bsdf_anisotropic_in, sh_node_bsdf_anisotropic_out);
node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
- node_type_init(&ntype, NULL);
+ node_type_init(&ntype, node_shader_init_anisotropic);
node_type_storage(&ntype, "", NULL, NULL);
node_type_gpu(&ntype, node_shader_gpu_bsdf_anisotropic);
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.c
index a59cbd6f46e..55dafaeca35 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.c
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.c
@@ -42,6 +42,11 @@ static bNodeSocketTemplate sh_node_bsdf_glass_out[] = {
{ -1, 0, "" }
};
+static void node_shader_init_glass(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ node->custom1 = SHD_GLOSSY_BECKMANN;
+}
+
static int node_shader_gpu_bsdf_glass(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
{
if (!in[3].link)
@@ -59,7 +64,7 @@ void register_node_type_sh_bsdf_glass(void)
node_type_compatibility(&ntype, NODE_NEW_SHADING);
node_type_socket_templates(&ntype, sh_node_bsdf_glass_in, sh_node_bsdf_glass_out);
node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
- node_type_init(&ntype, NULL);
+ node_type_init(&ntype, node_shader_init_glass);
node_type_storage(&ntype, "", NULL, NULL);
node_type_gpu(&ntype, node_shader_gpu_bsdf_glass);
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.c
index b8ad5e4fbed..9518784eebe 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.c
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.c
@@ -41,6 +41,11 @@ static bNodeSocketTemplate sh_node_bsdf_glossy_out[] = {
{ -1, 0, "" }
};
+static void node_shader_init_glossy(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ node->custom1 = SHD_GLOSSY_GGX;
+}
+
static int node_shader_gpu_bsdf_glossy(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
{
if (!in[2].link)
@@ -58,7 +63,7 @@ void register_node_type_sh_bsdf_glossy(void)
node_type_compatibility(&ntype, NODE_NEW_SHADING);
node_type_socket_templates(&ntype, sh_node_bsdf_glossy_in, sh_node_bsdf_glossy_out);
node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
- node_type_init(&ntype, NULL);
+ node_type_init(&ntype, node_shader_init_glossy);
node_type_storage(&ntype, "", NULL, NULL);
node_type_gpu(&ntype, node_shader_gpu_bsdf_glossy);
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair.c
index b48a4be9f7a..dbc8807a845 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair.c
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair.c
@@ -43,9 +43,6 @@ static bNodeSocketTemplate sh_node_bsdf_hair_out[] = {
static int node_shader_gpu_bsdf_hair(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
{
- if (!in[2].link)
- in[2].link = GPU_builtin(GPU_VIEW_NORMAL);
-
return GPU_stack_link(mat, "node_bsdf_hair", in, out);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.c
index 5d1bcc81adb..90db7a5771d 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.c
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.c
@@ -42,6 +42,11 @@ static bNodeSocketTemplate sh_node_bsdf_refraction_out[] = {
{ -1, 0, "" }
};
+static void node_shader_init_refraction(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ node->custom1 = SHD_GLOSSY_BECKMANN;
+}
+
static int node_shader_gpu_bsdf_refraction(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
{
if (!in[3].link)
@@ -59,7 +64,7 @@ void register_node_type_sh_bsdf_refraction(void)
node_type_compatibility(&ntype, NODE_NEW_SHADING);
node_type_socket_templates(&ntype, sh_node_bsdf_refraction_in, sh_node_bsdf_refraction_out);
node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
- node_type_init(&ntype, NULL);
+ node_type_init(&ntype, node_shader_init_refraction);
node_type_storage(&ntype, "", NULL, NULL);
node_type_gpu(&ntype, node_shader_gpu_bsdf_refraction);
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.c b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.c
new file mode 100644
index 00000000000..605a3b9faa3
--- /dev/null
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.c
@@ -0,0 +1,93 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2014 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Thomas Dinges
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/nodes/shader/nodes/node_shader_sepcombXYZ.c
+ * \ingroup shdnodes
+ */
+
+
+#include "node_shader_util.h"
+
+/* **************** SEPARATE XYZ ******************** */
+static bNodeSocketTemplate sh_node_sepxyz_in[] = {
+ { SOCK_VECTOR, 1, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f},
+ { -1, 0, "" }
+};
+static bNodeSocketTemplate sh_node_sepxyz_out[] = {
+ { SOCK_FLOAT, 0, N_("X")},
+ { SOCK_FLOAT, 0, N_("Y")},
+ { SOCK_FLOAT, 0, N_("Z")},
+ { -1, 0, "" }
+};
+
+static int gpu_shader_sepxyz(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
+{
+ return GPU_stack_link(mat, "separate_xyz", in, out);
+}
+
+void register_node_type_sh_sepxyz(void)
+{
+ static bNodeType ntype;
+
+ sh_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTOR, 0);
+ node_type_compatibility(&ntype, NODE_NEW_SHADING);
+ node_type_socket_templates(&ntype, sh_node_sepxyz_in, sh_node_sepxyz_out);
+ node_type_gpu(&ntype, gpu_shader_sepxyz);
+
+ nodeRegisterType(&ntype);
+}
+
+
+
+/* **************** COMBINE XYZ ******************** */
+static bNodeSocketTemplate sh_node_combxyz_in[] = {
+ { SOCK_FLOAT, 1, N_("X"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_UNSIGNED},
+ { SOCK_FLOAT, 1, N_("Y"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_UNSIGNED},
+ { SOCK_FLOAT, 1, N_("Z"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_UNSIGNED},
+ { -1, 0, "" }
+};
+static bNodeSocketTemplate sh_node_combxyz_out[] = {
+ { SOCK_VECTOR, 0, N_("Vector")},
+ { -1, 0, "" }
+};
+
+static int gpu_shader_combxyz(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
+{
+ return GPU_stack_link(mat, "combine_xyz", in, out);
+}
+
+void register_node_type_sh_combxyz(void)
+{
+ static bNodeType ntype;
+
+ sh_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTOR, 0);
+ node_type_compatibility(&ntype, NODE_NEW_SHADING);
+ node_type_socket_templates(&ntype, sh_node_combxyz_in, sh_node_combxyz_out);
+ node_type_gpu(&ntype, gpu_shader_combxyz);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c
index 33ff63a9a28..96b5707ec27 100644
--- a/source/blender/python/generic/py_capi_utils.c
+++ b/source/blender/python/generic/py_capi_utils.c
@@ -40,8 +40,9 @@
/* only for BLI_strncpy_wchar_from_utf8, should replace with py funcs but too late in release now */
#include "BLI_string_utf8.h"
-#ifdef _WIN32 /* BLI_setenv */
-#include "BLI_path_util.h"
+#ifdef _WIN32
+#include "BLI_path_util.h" /* BLI_setenv() */
+#include "BLI_math_base.h" /* finite() */
#endif
/* array utility function */
@@ -543,8 +544,9 @@ PyObject *PyC_DefaultNameSpace(const char *filename)
Py_DECREF(mod_main); /* sys.modules owns now */
PyModule_AddStringConstant(mod_main, "__name__", "__main__");
if (filename) {
- /* __file__ mainly for nice UI'ness */
- PyModule_AddObject(mod_main, "__file__", PyUnicode_DecodeFSDefault(filename));
+ /* __file__ mainly for nice UI'ness
+ * note: this wont map to a real file when executing text-blocks and buttons. */
+ PyModule_AddObject(mod_main, "__file__", PyC_UnicodeFromByte(filename));
}
PyModule_AddObject(mod_main, "__builtins__", interp->builtins);
Py_INCREF(interp->builtins); /* AddObject steals a reference */
@@ -605,8 +607,7 @@ void PyC_SetHomePath(const char *py_path_bundle)
/* cant use this, on linux gives bug: #23018, TODO: try LANG="en_US.UTF-8" /usr/bin/blender, suggested 22008 */
/* mbstowcs(py_path_bundle_wchar, py_path_bundle, FILE_MAXDIR); */
- BLI_strncpy_wchar_from_utf8(py_path_bundle_wchar, py_path_bundle,
- sizeof(py_path_bundle_wchar) / sizeof(wchar_t));
+ BLI_strncpy_wchar_from_utf8(py_path_bundle_wchar, py_path_bundle, ARRAY_SIZE(py_path_bundle_wchar));
Py_SetPythonHome(py_path_bundle_wchar);
// printf("found python (wchar_t) '%ls'\n", py_path_bundle_wchar);
@@ -904,3 +905,73 @@ PyObject *PyC_FlagSet_FromBitfield(PyC_FlagSet *items, int flag)
return ret;
}
+
+
+/**
+ * \return -1 on error, else 0
+ *
+ * \note it is caller's responsibility to acquire & release GIL!
+ */
+int PyC_RunString_AsNumber(const char *expr, double *value, const char *filename)
+{
+ PyObject *py_dict, *mod, *retval;
+ int error_ret = 0;
+ PyObject *main_mod = NULL;
+
+ PyC_MainModule_Backup(&main_mod);
+
+ py_dict = PyC_DefaultNameSpace(filename);
+
+ mod = PyImport_ImportModule("math");
+ if (mod) {
+ PyDict_Merge(py_dict, PyModule_GetDict(mod), 0); /* 0 - don't overwrite existing values */
+ Py_DECREF(mod);
+ }
+ else { /* highly unlikely but possibly */
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ retval = PyRun_String(expr, Py_eval_input, py_dict, py_dict);
+
+ if (retval == NULL) {
+ error_ret = -1;
+ }
+ else {
+ double val;
+
+ if (PyTuple_Check(retval)) {
+ /* Users my have typed in 10km, 2m
+ * add up all values */
+ int i;
+ val = 0.0;
+
+ for (i = 0; i < PyTuple_GET_SIZE(retval); i++) {
+ const double val_item = PyFloat_AsDouble(PyTuple_GET_ITEM(retval, i));
+ if (val_item == -1 && PyErr_Occurred()) {
+ val = -1;
+ break;
+ }
+ val += val_item;
+ }
+ }
+ else {
+ val = PyFloat_AsDouble(retval);
+ }
+ Py_DECREF(retval);
+
+ if (val == -1 && PyErr_Occurred()) {
+ error_ret = -1;
+ }
+ else if (!finite(val)) {
+ *value = 0.0;
+ }
+ else {
+ *value = val;
+ }
+ }
+
+ PyC_MainModule_Restore(main_mod);
+
+ return error_ret;
+}
diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h
index 0afc4dd98d9..559a8e15678 100644
--- a/source/blender/python/generic/py_capi_utils.h
+++ b/source/blender/python/generic/py_capi_utils.h
@@ -73,4 +73,6 @@ int PyC_FlagSet_ValueFromID(PyC_FlagSet *item, const char *identifier, int
int PyC_FlagSet_ToBitfield(PyC_FlagSet *items, PyObject *value, int *r_value, const char *error_prefix);
PyObject *PyC_FlagSet_FromBitfield(PyC_FlagSet *items, int flag);
+int PyC_RunString_AsNumber(const char *expr, double *value, const char *filename);
+
#endif /* __PY_CAPI_UTILS_H__ */
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index 25c4e630f58..a75db23d20b 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -68,6 +68,7 @@ set(SRC
bpy_rna_callback.c
bpy_traceback.c
bpy_util.c
+ bpy_utils_units.c
stubs.c
gpu.h
@@ -91,6 +92,7 @@ set(SRC
bpy_rna_callback.h
bpy_traceback.h
bpy_util.h
+ bpy_utils_units.h
../BPY_extern.h
)
diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c
index 89104821cb0..5fd19d3ed88 100644
--- a/source/blender/python/intern/bpy.c
+++ b/source/blender/python/intern/bpy.c
@@ -49,6 +49,7 @@
#include "bpy_props.h"
#include "bpy_library.h"
#include "bpy_operator.h"
+#include "bpy_utils_units.h"
#include "MEM_guardedalloc.h"
@@ -334,6 +335,7 @@ void BPy_init_modules(void)
/* ops is now a python module that does the conversion from SOME_OT_foo -> some.foo */
PyModule_AddObject(mod, "ops", BPY_operator_module());
PyModule_AddObject(mod, "app", BPY_app_struct());
+ PyModule_AddObject(mod, "_utils_units", BPY_utils_units());
/* bpy context */
RNA_pointer_create(NULL, &RNA_Context, (void *)BPy_GetContext(), &ctx_ptr);
diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c
index 795015c42a0..125a2e0c57d 100644
--- a/source/blender/python/intern/bpy_app.c
+++ b/source/blender/python/intern/bpy_app.c
@@ -107,7 +107,7 @@ static PyStructSequence_Desc app_info_desc = {
(char *)"bpy.app", /* name */
(char *)"This module contains application values that remain unchanged during runtime.", /* doc */
app_info_fields, /* fields */
- (sizeof(app_info_fields) / sizeof(PyStructSequence_Field)) - 1
+ ARRAY_SIZE(app_info_fields) - 1
};
static PyObject *make_app_info(void)
@@ -250,7 +250,7 @@ PyDoc_STRVAR(bpy_app_tempdir_doc,
);
static PyObject *bpy_app_tempdir_get(PyObject *UNUSED(self), void *UNUSED(closure))
{
- return PyC_UnicodeFromByte(BLI_temporary_dir());
+ return PyC_UnicodeFromByte(BLI_temp_dir_session());
}
PyDoc_STRVAR(bpy_app_driver_dict_doc,
diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c
index f43b67cf42f..022713558d7 100644
--- a/source/blender/python/intern/bpy_app_build_options.c
+++ b/source/blender/python/intern/bpy_app_build_options.c
@@ -26,6 +26,8 @@
#include <Python.h>
+#include "BLI_utildefines.h"
+
#include "bpy_app_build_options.h"
static PyTypeObject BlenderAppBuildOptionsType;
@@ -74,7 +76,7 @@ static PyStructSequence_Desc app_builtopts_info_desc = {
(char *)"bpy.app.build_options", /* name */
(char *)"This module contains information about options blender is built with", /* doc */
app_builtopts_info_fields, /* fields */
- (sizeof(app_builtopts_info_fields) / sizeof(PyStructSequence_Field)) - 1
+ ARRAY_SIZE(app_builtopts_info_fields) - 1
};
static PyObject *make_builtopts_info(void)
diff --git a/source/blender/python/intern/bpy_app_ffmpeg.c b/source/blender/python/intern/bpy_app_ffmpeg.c
index 2f7577928c5..fd516e4547f 100644
--- a/source/blender/python/intern/bpy_app_ffmpeg.c
+++ b/source/blender/python/intern/bpy_app_ffmpeg.c
@@ -60,7 +60,7 @@ static PyStructSequence_Desc app_ffmpeg_info_desc = {
(char *)"bpy.app.ffmpeg", /* name */
(char *)"This module contains information about FFmpeg blender is linked against", /* doc */
app_ffmpeg_info_fields, /* fields */
- (sizeof(app_ffmpeg_info_fields) / sizeof(PyStructSequence_Field)) - 1
+ ARRAY_SIZE(app_ffmpeg_info_fields) - 1
};
static PyObject *make_ffmpeg_info(void)
diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c
index b3be5a819fb..0c13230c365 100644
--- a/source/blender/python/intern/bpy_app_handlers.c
+++ b/source/blender/python/intern/bpy_app_handlers.c
@@ -71,11 +71,11 @@ static PyStructSequence_Desc app_cb_info_desc = {
(char *)"bpy.app.handlers", /* name */
(char *)"This module contains callbacks", /* doc */
app_cb_info_fields, /* fields */
- (sizeof(app_cb_info_fields) / sizeof(PyStructSequence_Field)) - 1
+ ARRAY_SIZE(app_cb_info_fields) - 1
};
#if 0
-# if (BLI_CB_EVT_TOT != ((sizeof(app_cb_info_fields) / sizeof(PyStructSequence_Field))))
+# if (BLI_CB_EVT_TOT != ARRAY_SIZE(app_cb_info_fields))
# error "Callbacks are out of sync"
# endif
#endif
diff --git a/source/blender/python/intern/bpy_app_ocio.c b/source/blender/python/intern/bpy_app_ocio.c
index eff52bc9dae..02e4044219a 100644
--- a/source/blender/python/intern/bpy_app_ocio.c
+++ b/source/blender/python/intern/bpy_app_ocio.c
@@ -46,7 +46,7 @@ static PyStructSequence_Desc app_ocio_info_desc = {
(char *)"bpy.app.ocio", /* name */
(char *)"This module contains information about OpenColorIO blender is linked against", /* doc */
app_ocio_info_fields, /* fields */
- (sizeof(app_ocio_info_fields) / sizeof(PyStructSequence_Field)) - 1
+ ARRAY_SIZE(app_ocio_info_fields) - 1
};
static PyObject *make_ocio_info(void)
diff --git a/source/blender/python/intern/bpy_app_oiio.c b/source/blender/python/intern/bpy_app_oiio.c
index b5f0f321c6d..60daf3ddd8b 100644
--- a/source/blender/python/intern/bpy_app_oiio.c
+++ b/source/blender/python/intern/bpy_app_oiio.c
@@ -46,7 +46,7 @@ static PyStructSequence_Desc app_oiio_info_desc = {
(char *)"bpy.app.oiio", /* name */
(char *)"This module contains information about OpeImageIO blender is linked against", /* doc */
app_oiio_info_fields, /* fields */
- (sizeof(app_oiio_info_fields) / sizeof(PyStructSequence_Field)) - 1
+ ARRAY_SIZE(app_oiio_info_fields) - 1
};
static PyObject *make_oiio_info(void)
diff --git a/source/blender/python/intern/bpy_app_translations.c b/source/blender/python/intern/bpy_app_translations.c
index e168bf33eb5..109e5606037 100644
--- a/source/blender/python/intern/bpy_app_translations.c
+++ b/source/blender/python/intern/bpy_app_translations.c
@@ -390,13 +390,13 @@ static BLF_i18n_contexts_descriptor _contexts[] = BLF_I18NCONTEXTS_DESC;
* This allows us to avoid many handwriting, and above all, to keep all context definition stuff in BLF_translation.h!
*/
static PyStructSequence_Field
-app_translations_contexts_fields[sizeof(_contexts) / sizeof(BLF_i18n_contexts_descriptor)] = {{NULL}};
+app_translations_contexts_fields[ARRAY_SIZE(_contexts)] = {{NULL}};
static PyStructSequence_Desc app_translations_contexts_desc = {
(char *)"bpy.app.translations.contexts", /* name */
(char *)"This named tuple contains all pre-defined translation contexts", /* doc */
app_translations_contexts_fields, /* fields */
- (sizeof(app_translations_contexts_fields) / sizeof(PyStructSequence_Field)) - 1
+ ARRAY_SIZE(app_translations_contexts_fields) - 1
};
static PyObject *app_translations_contexts_make(void)
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index 045acc65d1e..b7752b39900 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -243,7 +243,7 @@ void BPY_python_start(int argc, const char **argv)
/* not essential but nice to set our name */
static wchar_t program_path_wchar[FILE_MAX]; /* python holds a reference */
- BLI_strncpy_wchar_from_utf8(program_path_wchar, BLI_program_path(), sizeof(program_path_wchar) / sizeof(wchar_t));
+ BLI_strncpy_wchar_from_utf8(program_path_wchar, BLI_program_path(), ARRAY_SIZE(program_path_wchar));
Py_SetProgramName(program_path_wchar);
/* must run before python initializes */
@@ -566,15 +566,12 @@ void BPY_DECREF_RNA_INVALIDATE(void *pyob_ptr)
PyGILState_Release(gilstate);
}
-
/* return -1 on error, else 0 */
int BPY_button_exec(bContext *C, const char *expr, double *value, const bool verbose)
{
PyGILState_STATE gilstate;
- PyObject *py_dict, *mod, *retval;
int error_ret = 0;
- PyObject *main_mod = NULL;
-
+
if (!value || !expr) return -1;
if (expr[0] == '\0') {
@@ -584,59 +581,8 @@ int BPY_button_exec(bContext *C, const char *expr, double *value, const bool ver
bpy_context_set(C, &gilstate);
- PyC_MainModule_Backup(&main_mod);
-
- py_dict = PyC_DefaultNameSpace("<blender button>");
-
- mod = PyImport_ImportModule("math");
- if (mod) {
- PyDict_Merge(py_dict, PyModule_GetDict(mod), 0); /* 0 - don't overwrite existing values */
- Py_DECREF(mod);
- }
- else { /* highly unlikely but possibly */
- PyErr_Print();
- PyErr_Clear();
- }
-
- retval = PyRun_String(expr, Py_eval_input, py_dict, py_dict);
-
- if (retval == NULL) {
- error_ret = -1;
- }
- else {
- double val;
-
- if (PyTuple_Check(retval)) {
- /* Users my have typed in 10km, 2m
- * add up all values */
- int i;
- val = 0.0;
+ error_ret = PyC_RunString_AsNumber(expr, value, "<blender button>");
- for (i = 0; i < PyTuple_GET_SIZE(retval); i++) {
- const double val_item = PyFloat_AsDouble(PyTuple_GET_ITEM(retval, i));
- if (val_item == -1 && PyErr_Occurred()) {
- val = -1;
- break;
- }
- val += val_item;
- }
- }
- else {
- val = PyFloat_AsDouble(retval);
- }
- Py_DECREF(retval);
-
- if (val == -1 && PyErr_Occurred()) {
- error_ret = -1;
- }
- else if (!finite(val)) {
- *value = 0.0;
- }
- else {
- *value = val;
- }
- }
-
if (error_ret) {
if (verbose) {
BPy_errors_to_report(CTX_wm_reports(C));
@@ -646,10 +592,8 @@ int BPY_button_exec(bContext *C, const char *expr, double *value, const bool ver
}
}
- PyC_MainModule_Restore(main_mod);
-
bpy_context_clear(C, &gilstate);
-
+
return error_ret;
}
diff --git a/source/blender/python/intern/bpy_intern_string.c b/source/blender/python/intern/bpy_intern_string.c
index 9c93af1786c..fd32c91a480 100644
--- a/source/blender/python/intern/bpy_intern_string.c
+++ b/source/blender/python/intern/bpy_intern_string.c
@@ -69,12 +69,12 @@ void bpy_intern_string_init(void)
#undef BPY_INTERN_STR
- BLI_assert(i == (sizeof(bpy_intern_str_arr) / sizeof(*bpy_intern_str_arr)));
+ BLI_assert(i == ARRAY_SIZE(bpy_intern_str_arr));
}
void bpy_intern_string_exit(void)
{
- unsigned int i = sizeof(bpy_intern_str_arr) / sizeof(*bpy_intern_str_arr);
+ unsigned int i = ARRAY_SIZE(bpy_intern_str_arr);
while (i--) {
Py_DECREF(bpy_intern_str_arr[i]);
}
diff --git a/source/blender/python/intern/bpy_utils_units.c b/source/blender/python/intern/bpy_utils_units.c
new file mode 100644
index 00000000000..cdbd57bcebe
--- /dev/null
+++ b/source/blender/python/intern/bpy_utils_units.c
@@ -0,0 +1,332 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Bastien Montagne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/python/intern/bpy_utils_units.c
+ * \ingroup pythonintern
+ *
+ * This file defines a singleton py object accessed via 'bpy.utils.units',
+ * which exposes various data and functions useful in units handling.
+ */
+
+/* Future-proof, See https://docs.python.org/3/c-api/arg.html#strings-and-buffers */
+#define PY_SSIZE_T_CLEAN
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "BLI_utildefines.h"
+#include "BLI_string.h"
+#include "BLI_ghash.h"
+
+#include "BPY_extern.h"
+#include "bpy_utils_units.h"
+
+#include "../generic/py_capi_utils.h"
+
+#include "BKE_unit.h"
+
+/***** C-defined systems and types *****/
+
+static PyTypeObject BPyUnitsSystemsType;
+static PyTypeObject BPyUnitsCategoriesType;
+
+/* XXX Maybe better as externs of BKE_unit.h ? */
+static const char *bpyunits_usystem_items[] = {
+ "NONE",
+ "METRIC",
+ "IMPERIAL",
+ NULL,
+};
+
+static const char *bpyunits_ucategorie_items[] = {
+ "NONE",
+ "LENGTH",
+ "AREA",
+ "VOLUME",
+ "MASS",
+ "ROTATION",
+ "TIME",
+ "VELOCITY",
+ "ACCELERATION",
+ "CAMERA",
+ NULL,
+};
+
+/**
+ * These fields are just empty placeholders, actual values get set in initializations functions.
+ * This allows us to avoid many handwriting, and above all, to keep all systems/categories definition stuff in
+ * ``BKE_unit.h``.
+ */
+static PyStructSequence_Field bpyunits_systems_fields[ARRAY_SIZE(bpyunits_usystem_items)];
+static PyStructSequence_Field bpyunits_categories_fields[ARRAY_SIZE(bpyunits_ucategorie_items)];
+
+static PyStructSequence_Desc bpyunits_systems_desc = {
+ (char *)"bpy.utils.units.systems", /* name */
+ (char *)"This named tuple contains all pre-defined unit systems", /* doc */
+ bpyunits_systems_fields, /* fields */
+ ARRAY_SIZE(bpyunits_systems_fields) - 1
+};
+static PyStructSequence_Desc bpyunits_categories_desc = {
+ (char *)"bpy.utils.units.categories", /* name */
+ (char *)"This named tuple contains all pre-defined unit names", /* doc */
+ bpyunits_categories_fields, /* fields */
+ ARRAY_SIZE(bpyunits_categories_fields) - 1
+};
+
+/**
+ * Simple utility function to initialize #PyStructSequence_Desc
+ */
+static PyObject *py_structseq_from_strings(
+ PyTypeObject *py_type,
+ PyStructSequence_Desc *py_sseq_desc,
+ const char **str_items)
+{
+ PyObject *py_struct_seq;
+ int pos = 0;
+
+ const char **str_iter;
+ PyStructSequence_Field *desc;
+
+ /* initialize array */
+ /* We really populate the contexts' fields here! */
+ for (str_iter = str_items, desc = py_sseq_desc->fields; *str_iter; str_iter++, desc++) {
+ desc->name = (char *)*str_iter;
+ desc->doc = NULL;
+ }
+ /* end sentinel */
+ desc->name = desc->doc = NULL;
+
+ PyStructSequence_InitType(py_type, py_sseq_desc);
+
+ /* initialize pytype */
+ py_struct_seq = PyStructSequence_New(py_type);
+ BLI_assert(py_struct_seq != NULL);
+
+ for (str_iter = str_items; *str_iter; str_iter++) {
+ PyStructSequence_SET_ITEM(py_struct_seq, pos++, PyUnicode_FromString((*str_iter)));
+ }
+
+ return py_struct_seq;
+}
+
+static bool bpyunits_validate(const char *usys_str, const char *ucat_str, int *r_usys, int *r_ucat)
+{
+ *r_usys = BLI_str_index_in_array(usys_str, bpyunits_usystem_items);
+ if (*r_usys < 0) {
+ PyErr_Format(PyExc_ValueError,
+ "Unknown unit system specified: %.200s.",
+ usys_str);
+ return false;
+ }
+
+ *r_ucat = BLI_str_index_in_array(ucat_str, bpyunits_ucategorie_items);
+ if (*r_ucat < 0) {
+ PyErr_Format(PyExc_ValueError,
+ "Unknown unit category specified: %.200s.",
+ ucat_str);
+ return false;
+ }
+
+ if (!bUnit_IsValid(*r_usys, *r_ucat)) {
+ PyErr_Format(PyExc_ValueError,
+ "%.200s / %.200s unit system/category combination is not valid.",
+ usys_str, ucat_str);
+ return false;
+ }
+
+ return true;
+}
+
+PyDoc_STRVAR(bpyunits_to_value_doc,
+".. method:: to_value(unit_system, unit_category, str_input, [str_ref_unit=None])\n"
+"\n"
+" Convert a given input string into a float value.\n"
+"\n"
+" :arg unit_system: The unit system, from :attr:`bpy.utils.units.systems`.\n"
+" :type unit_system: string\n"
+" :arg unit_category: The category of data we are converting (length, area, rotation, etc.), "
+" from :attr:`bpy.utils.units.categories`.\n"
+" :type unit_category: string\n"
+" :arg str_input: The string to convert to a float value.\n"
+" :type str_input: string\n"
+" :arg str_ref_unit: A reference string from which to extract a default unit, if none is found in :arg:`str_input`.\n"
+" :type str_ref_unit: string or None\n"
+" :return: The converted/interpreted value.\n"
+" :rtype: float\n"
+" :raises ValueError: if conversion fails to generate a valid python float value.\n"
+);
+static PyObject *bpyunits_to_value(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
+{
+ static const char *kwlist[] = {"unit_system", "unit_category", "str_input", "str_ref_unit", NULL};
+
+ char *usys_str = NULL, *ucat_str = NULL, *inpt = NULL, *uref = NULL;
+ const float scale = 1.0f;
+
+ char *str;
+ Py_ssize_t str_len;
+ double result;
+ int usys, ucat;
+ PyObject *ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "sss#|z:bpy.utils.units.to_value", (char **)kwlist,
+ &usys_str, &ucat_str, &inpt, &str_len, &uref))
+ {
+ return NULL;
+ }
+
+ if (!bpyunits_validate(usys_str, ucat_str, &usys, &ucat)) {
+ return NULL;
+ }
+
+ str_len = str_len * 2 + 64;
+ str = PyMem_MALLOC(sizeof(*str) * (size_t)str_len);
+ BLI_strncpy(str, inpt, (size_t)str_len);
+
+ bUnit_ReplaceString(str, (int)str_len, uref, scale, usys, ucat);
+
+ if (PyC_RunString_AsNumber(str, &result, "<bpy_units_api>") != 0) {
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ PyErr_Format(PyExc_ValueError,
+ "'%.200s' (converted as '%s') could not be evaluated.",
+ inpt, str);
+ ret = NULL;
+ }
+ else {
+ ret = PyFloat_FromDouble(result);
+ }
+
+ PyMem_FREE(str);
+ return ret;
+}
+
+PyDoc_STRVAR(bpyunits_to_string_doc,
+".. method:: to_string(unit_system, unit_category, value, [precision=3, [split_unit=False, [compatible_unit=False]]])\n"
+"\n"
+" Convert a given input float value into a string with units.\n"
+"\n"
+" :arg unit_system: The unit system, from :attr:`bpy.utils.units.systems`.\n"
+" :type unit_system: string\n"
+" :arg unit_category: The category of data we are converting (length, area, rotation, etc.), "
+" from :attr:`bpy.utils.units.categories`.\n"
+" :type unit_category: string\n"
+" :arg value: The value to convert to a string.\n"
+" :type value: float\n"
+" :arg precision: Number of digits after the comma.\n"
+" :type precision: int\n"
+" :arg split_unit: Whether to use several units if needed (1m1cm), or always only one (1.01m).\n"
+" :type split_unit: bool\n"
+" :arg compatible_unit: Whether to use keyboard-friendly units (1m2) or nicer utf-8 ones (1m²).\n"
+" :type compatible_unit: bool\n"
+" :return: The converted string.\n"
+" :rtype: str\n"
+" :raises ValueError: if conversion fails to generate a valid python string.\n"
+);
+static PyObject *bpyunits_to_string(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
+{
+ static const char *kwlist[] = {"unit_system", "unit_category", "value",
+ "precision", "split_unit", "compatible_unit", NULL};
+
+ char *usys_str = NULL, *ucat_str = NULL;
+ double value = 0.0;
+ int precision = 3, split_unit = false, compatible_unit = false;
+
+ int usys, ucat;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "ssd|ipp:bpy.utils.units.to_string", (char **)kwlist,
+ &usys_str, &ucat_str, &value, &precision, &split_unit, &compatible_unit))
+ {
+ return NULL;
+ }
+
+ if (!bpyunits_validate(usys_str, ucat_str, &usys, &ucat)) {
+ return NULL;
+ }
+
+ {
+ /* Maximum expected length of string result:
+ * - number itself: precision + decimal dot + up to four 'above dot' digits.
+ * - unit: up to ten chars (six currently, let's be conservative, also because we use some utf8 chars).
+ * This can be repeated twice (e.g. 1m20cm), and we add ten more spare chars (spaces, trailing '\0'...).
+ * So in practice, 64 should be more than enough.
+ */
+ char buf1[64], buf2[64], *str;
+ PyObject *result;
+
+ bUnit_AsString(buf1, sizeof(buf1), value, precision, usys, ucat, (bool)split_unit, false);
+
+ if (compatible_unit) {
+ bUnit_ToUnitAltName(buf2, sizeof(buf2), buf1, usys, ucat);
+ str = buf2;
+ }
+ else {
+ str = buf1;
+ }
+
+ result = PyUnicode_FromString(str);
+
+ return result;
+ }
+}
+
+static PyMethodDef bpyunits_methods[] = {
+ {"to_value", (PyCFunction)bpyunits_to_value, METH_VARARGS | METH_KEYWORDS, bpyunits_to_value_doc},
+ {"to_string", (PyCFunction)bpyunits_to_string, METH_VARARGS | METH_KEYWORDS, bpyunits_to_string_doc},
+ {NULL, NULL, 0, NULL}
+};
+
+PyDoc_STRVAR(bpyunits_doc,
+"This module contains some data/methods regarding units handling."
+);
+
+static struct PyModuleDef bpyunits_module = {
+ PyModuleDef_HEAD_INIT,
+ "bpy.utils.units",
+ bpyunits_doc,
+ -1, /* multiple "initialization" just copies the module dict. */
+ bpyunits_methods,
+ NULL, NULL, NULL, NULL
+};
+
+PyObject *BPY_utils_units(void)
+{
+ PyObject *submodule, *item;
+
+ submodule = PyModule_Create(&bpyunits_module);
+ PyDict_SetItemString(PyImport_GetModuleDict(), bpyunits_module.m_name, submodule);
+ Py_INCREF(submodule);
+
+ /* Finalize our unit systems and types structseq definitions! */
+
+ /* bpy.utils.units.system */
+ item = py_structseq_from_strings(&BPyUnitsSystemsType, &bpyunits_systems_desc, bpyunits_usystem_items);
+ PyModule_AddObject(submodule, "systems", item); /* steals ref */
+
+ /* bpy.utils.units.categories */
+ item = py_structseq_from_strings(&BPyUnitsCategoriesType, &bpyunits_categories_desc, bpyunits_ucategorie_items);
+ PyModule_AddObject(submodule, "categories", item); /* steals ref */
+
+ return submodule;
+}
diff --git a/source/blender/python/intern/bpy_utils_units.h b/source/blender/python/intern/bpy_utils_units.h
new file mode 100644
index 00000000000..5f840a2304d
--- /dev/null
+++ b/source/blender/python/intern/bpy_utils_units.h
@@ -0,0 +1,32 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Bastien Montagne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/python/intern/bpy_utils_units.h
+ * \ingroup pythonintern
+ */
+
+#ifndef __BPY_UTILS_UNITS_H__
+#define __BPY_UTILS_UNITS_H__
+
+PyObject *BPY_utils_units(void);
+
+#endif /* __BPY_UTILS_UNITS_H__ */
diff --git a/source/blender/render/extern/include/RE_bake.h b/source/blender/render/extern/include/RE_bake.h
index 0f82082911a..8f2a0e382a4 100644
--- a/source/blender/render/extern/include/RE_bake.h
+++ b/source/blender/render/extern/include/RE_bake.h
@@ -80,14 +80,14 @@ bool RE_bake_internal(
struct Render *re, struct Object *object, const BakePixel pixel_array[],
const int num_pixels, const int depth, const ScenePassType pass_type, float result[]);
-void RE_bake_pixels_populate_from_objects(
+bool RE_bake_pixels_populate_from_objects(
struct Mesh *me_low, BakePixel pixel_array_from[],
- BakeHighPolyData highpoly[], const int tot_highpoly, const int num_pixels,
- const float cage_extrusion, float mat_low[4][4]);
+ BakeHighPolyData highpoly[], const int tot_highpoly, const int num_pixels, const bool is_custom_cage,
+ const float cage_extrusion, float mat_low[4][4], float mat_cage[4][4], struct Mesh *me_cage);
void RE_bake_pixels_populate(
struct Mesh *me, struct BakePixel *pixel_array,
- const int num_pixels, const struct BakeImages *bake_images);
+ const int num_pixels, const struct BakeImages *bake_images, const char *uv_layer);
void RE_bake_mask_fill(const BakePixel pixel_array[], const int num_pixels, char *mask);
diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h
index 031d6b5a51a..68ac0cd3073 100644
--- a/source/blender/render/extern/include/RE_pipeline.h
+++ b/source/blender/render/extern/include/RE_pipeline.h
@@ -190,7 +190,10 @@ struct RenderLayer *RE_GetRenderLayer(struct RenderResult *rr, const char *name)
float *RE_RenderLayerGetPass(struct RenderLayer *rl, int passtype);
/* obligatory initialize call, disprect is optional */
-void RE_InitState(struct Render *re, struct Render *source, struct RenderData *rd, struct SceneRenderLayer *srl, int winx, int winy, rcti *disprect);
+void RE_InitState(struct Render *re, struct Render *source, struct RenderData *rd,
+ struct SceneRenderLayer *srl,
+ int winx, int winy, rcti *disprect);
+void RE_ChangeResolution(struct Render *re, int winx, int winy, rcti *disprect);
/* set up the viewplane/perspective matrix, three choices */
struct Object *RE_GetCamera(struct Render *re); /* return camera override if set */
diff --git a/source/blender/render/intern/include/renderpipeline.h b/source/blender/render/intern/include/renderpipeline.h
index e713d4880d6..a831ab3c29c 100644
--- a/source/blender/render/intern/include/renderpipeline.h
+++ b/source/blender/render/intern/include/renderpipeline.h
@@ -34,11 +34,13 @@
#define __RENDERPIPELINE_H__
struct Render;
+struct RenderData;
struct RenderLayer;
struct RenderResult;
struct RenderLayer *render_get_active_layer(struct Render *re, struct RenderResult *rr);
float panorama_pixel_rot(struct Render *re);
+void render_update_anim_renderdata(struct Render *re, struct RenderData *rd);
#endif /* __RENDERPIPELINE_H__ */
diff --git a/source/blender/render/intern/source/bake_api.c b/source/blender/render/intern/source/bake_api.c
index 48516ea895c..efa8c9b98f4 100644
--- a/source/blender/render/intern/source/bake_api.c
+++ b/source/blender/render/intern/source/bake_api.c
@@ -158,19 +158,66 @@ void RE_bake_margin(ImBuf *ibuf, char *mask, const int margin)
IMB_rectfill_alpha(ibuf, 1.0f);
}
+
/**
* This function returns the coordinate and normal of a barycentric u,v for a face defined by the primitive_id index.
+ * The returned normal is actually the direction from the same barycentric coordinate in the cage to the base mesh
+ * The returned coordinate is the point in the cage mesh
*/
-static void calc_point_from_barycentric(
- TriTessFace *triangles, int primitive_id, float u, float v, float cage_extrusion,
+static void calc_point_from_barycentric_cage(
+ TriTessFace *triangles_low, TriTessFace *triangles_cage,
+ float mat_low[4][4], float mat_cage[4][4],
+ int primitive_id, float u, float v,
float r_co[3], float r_dir[3])
{
+ float data[2][3][3];
+ float coord[2][3];
+ float dir[3];
+ int i;
+
+ TriTessFace *triangle[2];
+
+ triangle[0] = &triangles_low[primitive_id];
+ triangle[1] = &triangles_cage[primitive_id];
+
+ for (i = 0; i < 2; i++) {
+ copy_v3_v3(data[i][0], triangle[i]->mverts[0]->co);
+ copy_v3_v3(data[i][1], triangle[i]->mverts[1]->co);
+ copy_v3_v3(data[i][2], triangle[i]->mverts[2]->co);
+ interp_barycentric_tri_v3(data[i], u, v, coord[i]);
+ }
+
+ /* convert from local to world space */
+ mul_m4_v3(mat_low, coord[0]);
+ mul_m4_v3(mat_cage, coord[1]);
+
+ sub_v3_v3v3(dir, coord[0], coord[1]);
+ normalize_v3(dir);
+
+ copy_v3_v3(r_co, coord[1]);
+ copy_v3_v3(r_dir, dir);
+}
+
+/**
+ * This function returns the coordinate and normal of a barycentric u,v for a face defined by the primitive_id index.
+ * The returned coordinate is extruded along the normal by cage_extrusion
+ */
+static void calc_point_from_barycentric_extrusion(
+ TriTessFace *triangles,
+ float mat[4][4], float imat[4][4],
+ int primitive_id, float u, float v,
+ float cage_extrusion,
+ float r_co[3], float r_dir[3],
+ const bool is_cage)
+{
float data[3][3];
float coord[3];
float dir[3];
float cage[3];
+ bool is_smooth;
TriTessFace *triangle = &triangles[primitive_id];
+ is_smooth = triangle->is_smooth || is_cage;
copy_v3_v3(data[0], triangle->mverts[0]->co);
copy_v3_v3(data[1], triangle->mverts[1]->co);
@@ -178,19 +225,29 @@ static void calc_point_from_barycentric(
interp_barycentric_tri_v3(data, u, v, coord);
- normal_short_to_float_v3(data[0], triangle->mverts[0]->no);
- normal_short_to_float_v3(data[1], triangle->mverts[1]->no);
- normal_short_to_float_v3(data[2], triangle->mverts[2]->no);
+ if (is_smooth) {
+ normal_short_to_float_v3(data[0], triangle->mverts[0]->no);
+ normal_short_to_float_v3(data[1], triangle->mverts[1]->no);
+ normal_short_to_float_v3(data[2], triangle->mverts[2]->no);
- interp_barycentric_tri_v3(data, u, v, dir);
- normalize_v3_v3(cage, dir);
- mul_v3_fl(cage, cage_extrusion);
+ interp_barycentric_tri_v3(data, u, v, dir);
+ normalize_v3(dir);
+ }
+ else {
+ copy_v3_v3(dir, triangle->normal);
+ }
+ mul_v3_v3fl(cage, dir, cage_extrusion);
add_v3_v3(coord, cage);
- normalize_v3_v3(dir, dir);
+ normalize_v3(dir);
negate_v3(dir);
+ /* convert from local to world space */
+ mul_m4_v3(mat, coord);
+ mul_transposed_mat3_m4_v3(imat, dir);
+ normalize_v3(dir);
+
copy_v3_v3(r_co, coord);
copy_v3_v3(r_dir, dir);
}
@@ -242,7 +299,9 @@ static bool cast_ray_highpoly(
normalize_v3(dir_high);
/* cast ray */
- BLI_bvhtree_ray_cast(treeData[i].tree, co_high, dir_high, 0.0f, &hits[i], treeData[i].raycast_callback, &treeData[i]);
+ if (treeData[i].tree) {
+ BLI_bvhtree_ray_cast(treeData[i].tree, co_high, dir_high, 0.0f, &hits[i], treeData[i].raycast_callback, &treeData[i]);
+ }
if (hits[i].index != -1) {
/* cull backface */
@@ -377,32 +436,50 @@ static void mesh_calc_tri_tessface(
BLI_assert(p_id < me->totface * 2);
}
-void RE_bake_pixels_populate_from_objects(
+bool RE_bake_pixels_populate_from_objects(
struct Mesh *me_low, BakePixel pixel_array_from[],
- BakeHighPolyData highpoly[], const int tot_highpoly, const int num_pixels,
- const float cage_extrusion, float mat_low[4][4])
+ BakeHighPolyData highpoly[], const int tot_highpoly, const int num_pixels, const bool is_custom_cage,
+ const float cage_extrusion, float mat_low[4][4], float mat_cage[4][4], struct Mesh *me_cage)
{
int i;
int primitive_id;
float u, v;
float imat_low [4][4];
+ bool is_cage = me_cage != NULL;
+ bool result = true;
+ DerivedMesh *dm_low = NULL;
DerivedMesh **dm_highpoly;
BVHTreeFromMesh *treeData;
/* Note: all coordinates are in local space */
- TriTessFace *tris_low;
+ TriTessFace *tris_low = NULL;
+ TriTessFace *tris_cage = NULL;
TriTessFace **tris_high;
/* assume all lowpoly tessfaces can be quads */
- tris_low = MEM_mallocN(sizeof(TriTessFace) * (me_low->totface * 2), "MVerts Lowpoly Mesh");
- tris_high = MEM_mallocN(sizeof(TriTessFace *) * tot_highpoly, "MVerts Highpoly Mesh Array");
+ tris_high = MEM_callocN(sizeof(TriTessFace *) * tot_highpoly, "MVerts Highpoly Mesh Array");
/* assume all highpoly tessfaces are triangles */
dm_highpoly = MEM_callocN(sizeof(DerivedMesh *) * tot_highpoly, "Highpoly Derived Meshes");
treeData = MEM_callocN(sizeof(BVHTreeFromMesh) * tot_highpoly, "Highpoly BVH Trees");
- mesh_calc_tri_tessface(tris_low, me_low, false, NULL);
+ if (!is_cage) {
+ dm_low = CDDM_from_mesh(me_low);
+ tris_low = MEM_mallocN(sizeof(TriTessFace) * (me_low->totface * 2), "MVerts Lowpoly Mesh");
+ mesh_calc_tri_tessface(tris_low, me_low, true, dm_low);
+ }
+ else if (is_custom_cage) {
+ tris_low = MEM_mallocN(sizeof(TriTessFace) * (me_low->totface * 2), "MVerts Lowpoly Mesh");
+ mesh_calc_tri_tessface(tris_low, me_low, false, NULL);
+
+ tris_cage = MEM_mallocN(sizeof(TriTessFace) * (me_low->totface * 2), "MVerts Cage Mesh");
+ mesh_calc_tri_tessface(tris_cage, me_cage, false, NULL);
+ }
+ else {
+ tris_cage = MEM_mallocN(sizeof(TriTessFace) * (me_low->totface * 2), "MVerts Cage Mesh");
+ mesh_calc_tri_tessface(tris_cage, me_cage, false, NULL);
+ }
invert_m4_m4(imat_low, mat_low);
@@ -412,12 +489,15 @@ void RE_bake_pixels_populate_from_objects(
dm_highpoly[i] = CDDM_from_mesh(highpoly[i].me);
- /* Create a bvh-tree for each highpoly object */
- bvhtree_from_mesh_faces(&treeData[i], dm_highpoly[i], 0.0, 2, 6);
+ if (dm_highpoly[i]->getNumTessFaces(dm_highpoly[i]) != 0) {
+ /* Create a bvh-tree for each highpoly object */
+ bvhtree_from_mesh_faces(&treeData[i], dm_highpoly[i], 0.0, 2, 6);
- if (&treeData[i].tree == NULL) {
- printf("Baking: Out of memory\n");
- goto cleanup;
+ if (treeData[i].tree == NULL) {
+ printf("Baking: out of memory while creating BHVTree for object \"%s\"\n", highpoly[i].ob->id.name + 2);
+ result = false;
+ goto cleanup;
+ }
}
}
@@ -439,12 +519,15 @@ void RE_bake_pixels_populate_from_objects(
v = pixel_array_from[i].uv[1];
/* calculate from low poly mesh cage */
- calc_point_from_barycentric(tris_low, primitive_id, u, v, cage_extrusion, co, dir);
-
- /* convert from local to world space */
- mul_m4_v3(mat_low, co);
- mul_transposed_mat3_m4_v3(imat_low, dir);
- normalize_v3(dir);
+ if (is_custom_cage) {
+ calc_point_from_barycentric_cage(tris_low, tris_cage, mat_low, mat_cage, primitive_id, u, v, co, dir);
+ }
+ else if (is_cage) {
+ calc_point_from_barycentric_extrusion(tris_cage, mat_low, imat_low, primitive_id, u, v, cage_extrusion, co, dir, true);
+ }
+ else {
+ calc_point_from_barycentric_extrusion(tris_low, mat_low, imat_low, primitive_id, u, v, cage_extrusion, co, dir, false);
+ }
/* cast ray */
if (!cast_ray_highpoly(treeData, tris_high, highpoly, co, dir, i, tot_highpoly,
@@ -460,14 +543,31 @@ void RE_bake_pixels_populate_from_objects(
cleanup:
for (i = 0; i < tot_highpoly; i++) {
free_bvhtree_from_mesh(&treeData[i]);
- dm_highpoly[i]->release(dm_highpoly[i]);
- MEM_freeN(tris_high[i]);
+
+ if (dm_highpoly[i]) {
+ dm_highpoly[i]->release(dm_highpoly[i]);
+ }
+
+ if (tris_high[i]) {
+ MEM_freeN(tris_high[i]);
+ }
}
- MEM_freeN(tris_low);
MEM_freeN(tris_high);
MEM_freeN(treeData);
MEM_freeN(dm_highpoly);
+
+ if (dm_low) {
+ dm_low->release(dm_low);
+ }
+ if (tris_low) {
+ MEM_freeN(tris_low);
+ }
+ if (tris_cage) {
+ MEM_freeN(tris_cage);
+ }
+
+ return result;
}
static void bake_differentials(BakeDataZSpan *bd, const float *uv1, const float *uv2, const float *uv3)
@@ -494,7 +594,7 @@ static void bake_differentials(BakeDataZSpan *bd, const float *uv1, const float
void RE_bake_pixels_populate(
Mesh *me, BakePixel pixel_array[],
- const int num_pixels, const BakeImages *bake_images)
+ const int num_pixels, const BakeImages *bake_images, const char *uv_layer)
{
BakeDataZSpan bd;
int i, a;
@@ -519,7 +619,14 @@ void RE_bake_pixels_populate(
zbuf_alloc_span(&bd.zspan[i], bake_images->data[i].width, bake_images->data[i].height, R.clipcrop);
}
- mtface = CustomData_get_layer(&me->fdata, CD_MTFACE);
+ if ((uv_layer == NULL) || (uv_layer[0] == '\0')) {
+ mtface = CustomData_get_layer(&me->fdata, CD_MTFACE);
+ }
+ else {
+ int uv_id = CustomData_get_named_layer(&me->fdata, CD_MTFACE, uv_layer);
+ mtface = CustomData_get_layer_n(&me->fdata, CD_MTFACE, uv_id);
+ }
+
mface = CustomData_get_layer(&me->fdata, CD_MFACE);
if (mtface == NULL)
diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c
index b28d1debfec..3e72ef06a67 100644
--- a/source/blender/render/intern/source/convertblender.c
+++ b/source/blender/render/intern/source/convertblender.c
@@ -5146,8 +5146,10 @@ void RE_Database_FromScene(Render *re, Main *bmain, Scene *scene, unsigned int l
lay &= 0xFF000000;
/* applies changes fully */
- if ((re->r.scemode & (R_NO_FRAME_UPDATE|R_BUTS_PREVIEW|R_VIEWPORT_PREVIEW))==0)
+ if ((re->r.scemode & (R_NO_FRAME_UPDATE|R_BUTS_PREVIEW|R_VIEWPORT_PREVIEW))==0) {
BKE_scene_update_for_newframe(re->eval_ctx, re->main, re->scene, lay);
+ render_update_anim_renderdata(re, &re->scene->r);
+ }
/* if no camera, viewmat should have been set! */
if (use_camera_view && camera) {
diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c
index c8fa8a75672..1df701c48eb 100644
--- a/source/blender/render/intern/source/external_engine.c
+++ b/source/blender/render/intern/source/external_engine.c
@@ -60,6 +60,7 @@
#include "RE_bake.h"
#include "initrender.h"
+#include "renderpipeline.h"
#include "render_types.h"
#include "render_result.h"
@@ -411,8 +412,8 @@ void RE_bake_engine_set_engine_parameters(Render *re, Main *bmain, Scene *scene)
re->r = scene->r;
/* prevent crash when freeing the scene
- but it potentially leaves unfreed memory blocks
- not sure how to fix this yet -- dfelinto */
+ * but it potentially leaves unfreed memory blocks
+ * not sure how to fix this yet -- dfelinto */
BLI_listbase_clear(&re->r.layers);
}
@@ -575,6 +576,7 @@ int RE_engine_render(Render *re, int do_all)
}
BKE_scene_update_for_newframe(re->eval_ctx, re->main, re->scene, lay);
+ render_update_anim_renderdata(re, &re->scene->r);
}
/* create render result */
diff --git a/source/blender/render/intern/source/occlusion.c b/source/blender/render/intern/source/occlusion.c
index dd0b1f89da7..f0fe5d054f0 100644
--- a/source/blender/render/intern/source/occlusion.c
+++ b/source/blender/render/intern/source/occlusion.c
@@ -621,7 +621,7 @@ static void occ_build_recursive(OcclusionTree *tree, OccNode *node, int begin, i
static void occ_build_sh_normalize(OccNode *node)
{
/* normalize spherical harmonics to not include area, so
- * we can clamp the dot product and then mutliply by area */
+ * we can clamp the dot product and then multiply by area */
int b;
if (node->area != 0.0f)
diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c
index 537714ace0b..bdd911df33b 100644
--- a/source/blender/render/intern/source/pipeline.c
+++ b/source/blender/render/intern/source/pipeline.c
@@ -511,32 +511,9 @@ static int check_mode_full_sample(RenderData *rd)
return scemode;
}
-/* what doesn't change during entire render sequence */
-/* disprect is optional, if NULL it assumes full window render */
-void RE_InitState(Render *re, Render *source, RenderData *rd, SceneRenderLayer *srl, int winx, int winy, rcti *disprect)
+static void re_init_resolution(Render *re, Render *source,
+ int winx, int winy, rcti *disprect)
{
- bool had_freestyle = (re->r.mode & R_EDGE_FRS) != 0;
-
- re->ok = true; /* maybe flag */
-
- re->i.starttime = PIL_check_seconds_timer();
-
- /* copy render data and render layers for thread safety */
- BLI_freelistN(&re->r.layers);
- re->r = *rd;
- BLI_duplicatelist(&re->r.layers, &rd->layers);
-
- if (source) {
- /* reuse border flags from source renderer */
- re->r.mode &= ~(R_BORDER | R_CROP);
- re->r.mode |= source->r.mode & (R_BORDER | R_CROP);
-
- /* dimensions shall be shared between all renderers */
- re->r.xsch = source->r.xsch;
- re->r.ysch = source->r.ysch;
- re->r.size = source->r.size;
- }
-
re->winx = winx;
re->winy = winy;
if (source && (source->r.mode & R_BORDER)) {
@@ -570,7 +547,41 @@ void RE_InitState(Render *re, Render *source, RenderData *rd, SceneRenderLayer *
re->rectx = winx;
re->recty = winy;
}
+
+ /* we clip faces with a minimum of 2 pixel boundary outside of image border. see zbuf.c */
+ re->clipcrop = 1.0f + 2.0f / (float)(re->winx > re->winy ? re->winy : re->winx);
+}
+
+/* what doesn't change during entire render sequence */
+/* disprect is optional, if NULL it assumes full window render */
+void RE_InitState(Render *re, Render *source, RenderData *rd,
+ SceneRenderLayer *srl,
+ int winx, int winy, rcti *disprect)
+{
+ bool had_freestyle = (re->r.mode & R_EDGE_FRS) != 0;
+
+ re->ok = true; /* maybe flag */
+ re->i.starttime = PIL_check_seconds_timer();
+
+ /* copy render data and render layers for thread safety */
+ BLI_freelistN(&re->r.layers);
+ re->r = *rd;
+ BLI_duplicatelist(&re->r.layers, &rd->layers);
+
+ if (source) {
+ /* reuse border flags from source renderer */
+ re->r.mode &= ~(R_BORDER | R_CROP);
+ re->r.mode |= source->r.mode & (R_BORDER | R_CROP);
+
+ /* dimensions shall be shared between all renderers */
+ re->r.xsch = source->r.xsch;
+ re->r.ysch = source->r.ysch;
+ re->r.size = source->r.size;
+ }
+
+ re_init_resolution(re, source, winx, winy, disprect);
+
if (re->rectx < 1 || re->recty < 1 || (BKE_imtype_is_movie(rd->im_format.imtype) &&
(re->rectx < 16 || re->recty < 16) ))
{
@@ -654,17 +665,72 @@ void RE_InitState(Render *re, Render *source, RenderData *rd, SceneRenderLayer *
BLI_rw_mutex_unlock(&re->resultmutex);
- /* we clip faces with a minimum of 2 pixel boundary outside of image border. see zbuf.c */
- re->clipcrop = 1.0f + 2.0f / (float)(re->winx > re->winy ? re->winy : re->winx);
-
re->mblur_offs = re->field_offs = 0.f;
RE_init_threadcount(re);
}
+static void render_result_rescale(Render *re)
+{
+ RenderResult *result = re->result;
+ int x, y;
+ float scale_x, scale_y;
+ float *src_rectf;
+
+ src_rectf = result->rectf;
+ if (src_rectf == NULL) {
+ RenderLayer *rl = render_get_active_layer(re, re->result);
+ if (rl != NULL) {
+ src_rectf = rl->rectf;
+ }
+ }
+
+ if (src_rectf != NULL) {
+ float *dst_rectf = NULL;
+ re->result = render_result_new(re,
+ &re->disprect,
+ 0,
+ RR_USE_MEM,
+ RR_ALL_LAYERS);
+
+ dst_rectf = re->result->rectf;
+ if (dst_rectf == NULL) {
+ RenderLayer *rl;
+ rl = render_get_active_layer(re, re->result);
+ if (rl != NULL) {
+ dst_rectf = rl->rectf;
+ }
+ }
+
+ scale_x = (float) result->rectx / re->result->rectx;
+ scale_y = (float) result->recty / re->result->recty;
+ for (x = 0; x < re->result->rectx; ++x) {
+ for (y = 0; y < re->result->recty; ++y) {
+ int src_x = x * scale_x,
+ src_y = y * scale_y;
+ int dst_index = y * re->result->rectx + x,
+ src_index = src_y * result->rectx + src_x;
+ copy_v4_v4(dst_rectf + dst_index * 4,
+ src_rectf + src_index * 4);
+ }
+ }
+ }
+
+ render_result_free(result);
+}
+
+void RE_ChangeResolution(Render *re, int winx, int winy, rcti *disprect)
+{
+ re_init_resolution(re, NULL, winx, winy, disprect);
+
+ BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
+ render_result_rescale(re);
+ BLI_rw_mutex_unlock(&re->resultmutex);
+}
+
/* update some variables that can be animated, and otherwise wouldn't be due to
* RenderData getting copied once at the start of animation render */
-static void render_update_anim_renderdata(Render *re, RenderData *rd)
+void render_update_anim_renderdata(Render *re, RenderData *rd)
{
/* filter */
re->r.gauss = rd->gauss;
diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c
index 40de1080634..dd867852bdb 100644
--- a/source/blender/render/intern/source/render_result.c
+++ b/source/blender/render/intern/source/render_result.c
@@ -41,7 +41,6 @@
#include "BLI_rect.h"
#include "BLI_string.h"
#include "BLI_system.h"
-#include BLI_SYSTEM_PID_H
#include "BLI_threads.h"
#include "BKE_image.h"
@@ -1030,14 +1029,13 @@ void render_result_exr_file_path(Scene *scene, const char *layname, int sample,
BLI_split_file_part(G.main->name, fi, sizeof(fi));
if (sample == 0) {
- BLI_snprintf(name, sizeof(name), "%s_%s_%s_%d.exr", fi, scene->id.name + 2, layname, abs(getpid()));
+ BLI_snprintf(name, sizeof(name), "%s_%s_%s.exr", fi, scene->id.name + 2, layname);
}
else {
- BLI_snprintf(name, sizeof(name), "%s_%s_%s%d_%d.exr", fi, scene->id.name + 2, layname, sample,
- abs(getpid()));
+ BLI_snprintf(name, sizeof(name), "%s_%s_%s%d.exr", fi, scene->id.name + 2, layname, sample);
}
- BLI_make_file_string("/", filepath, BLI_temporary_dir(), name);
+ BLI_make_file_string("/", filepath, BLI_temp_dir_session(), name);
}
/* only for temp buffer files, makes exact copy of render result */
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 9e50b01cd23..9886d403f74 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -118,7 +118,7 @@ void WM_cursor_modal_set(struct wmWindow *win, int curs);
void WM_cursor_modal_restore(struct wmWindow *win);
void WM_cursor_wait (bool val);
void WM_cursor_grab_enable(struct wmWindow *win, bool wrap, bool hide, int bounds[4]);
-void WM_cursor_grab_disable(struct wmWindow *win, int mouse_ungrab_xy[2]);
+void WM_cursor_grab_disable(struct wmWindow *win, const int mouse_ungrab_xy[2]);
void WM_cursor_time (struct wmWindow *win, int nr);
void *WM_paint_cursor_activate(struct wmWindowManager *wm,
diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c
index 6fd3b426142..68fd32cb450 100644
--- a/source/blender/windowmanager/intern/wm_cursors.c
+++ b/source/blender/windowmanager/intern/wm_cursors.c
@@ -230,7 +230,7 @@ void WM_cursor_grab_enable(wmWindow *win, bool wrap, bool hide, int bounds[4])
}
}
-void WM_cursor_grab_disable(wmWindow *win, int mouse_ungrab_xy[2])
+void WM_cursor_grab_disable(wmWindow *win, const int mouse_ungrab_xy[2])
{
if ((G.debug & G_DEBUG) == 0) {
if (win && win->ghostwin) {
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 175a8d20895..4b2ec0ef587 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -3349,7 +3349,7 @@ void WM_set_locked_interface(wmWindowManager *wm, bool lock)
*
* TODO(sergey): Make it different locked states, so different jobs
* could lock different areas of blender and allow
- * interation with others?
+ * interaction with others?
*/
BKE_spacedata_draw_locks(lock);
}
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 1ec2e6a3ee0..0bc6442348c 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -299,7 +299,7 @@ static void wm_init_userdef(bContext *C, const bool from_memory)
}
/* update tempdir from user preferences */
- BLI_init_temporary_dir(U.tempdir);
+ BLI_temp_dir_init(U.tempdir);
BKE_userdef_state();
}
@@ -591,7 +591,7 @@ int wm_homefile_read(bContext *C, ReportList *reports, bool from_memory, const c
if (BLI_listbase_is_empty(&wmbase)) {
wm_clear_default_size(C);
}
- BLI_init_temporary_dir(U.tempdir);
+ BLI_temp_dir_init(U.tempdir);
#ifdef WITH_PYTHON_SECURITY
/* use alternative setting for security nuts
@@ -1058,14 +1058,14 @@ void wm_autosave_location(char *filepath)
* BLI_make_file_string will create string that has it most likely on C:\
* through get_default_root().
* If there is no C:\tmp autosave fails. */
- if (!BLI_exists(BLI_temporary_dir())) {
+ if (!BLI_exists(BLI_temp_dir_base())) {
savedir = BLI_get_folder_create(BLENDER_USER_AUTOSAVE, NULL);
BLI_make_file_string("/", filepath, savedir, pidstr);
return;
}
#endif
- BLI_make_file_string("/", filepath, BLI_temporary_dir(), pidstr);
+ BLI_make_file_string("/", filepath, BLI_temp_dir_base(), pidstr);
}
void WM_autosave_init(wmWindowManager *wm)
@@ -1129,7 +1129,7 @@ void wm_autosave_delete(void)
if (BLI_exists(filename)) {
char str[FILE_MAX];
- BLI_make_file_string("/", str, BLI_temporary_dir(), BLENDER_QUIT_FILE);
+ BLI_make_file_string("/", str, BLI_temp_dir_base(), BLENDER_QUIT_FILE);
/* if global undo; remove tempsave, otherwise rename */
if (U.uiflag & USER_GLOBALUNDO) BLI_delete(filename, false, false);
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 5dafadc5a47..693c48cf8b9 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -117,11 +117,17 @@
static void wm_init_reports(bContext *C)
{
- BKE_reports_init(CTX_wm_reports(C), RPT_STORE);
+ ReportList *reports = CTX_wm_reports(C);
+
+ BLI_assert(!reports || BLI_listbase_is_empty(&reports->list));
+
+ BKE_reports_init(reports, RPT_STORE);
}
static void wm_free_reports(bContext *C)
{
- BKE_reports_clear(CTX_wm_reports(C));
+ ReportList *reports = CTX_wm_reports(C);
+
+ BKE_reports_clear(reports);
}
bool wm_start_with_console = false; /* used in creator.c */
@@ -399,7 +405,7 @@ void WM_exit_ext(bContext *C, const bool do_python)
/* save the undo state as quit.blend */
char filename[FILE_MAX];
- BLI_make_file_string("/", filename, BLI_temporary_dir(), BLENDER_QUIT_FILE);
+ BLI_make_file_string("/", filename, BLI_temp_dir_base(), BLENDER_QUIT_FILE);
if (BKE_undo_save_file(filename))
printf("Saved session recovery to '%s'\n", filename);
@@ -490,9 +496,11 @@ void WM_exit_ext(bContext *C, const bool do_python)
(void)do_python;
#endif
- GPU_global_buffer_pool_free();
- GPU_free_unused_buffers();
- GPU_extensions_exit();
+ if (!G.background) {
+ GPU_global_buffer_pool_free();
+ GPU_free_unused_buffers();
+ GPU_extensions_exit();
+ }
BKE_reset_undo();
@@ -519,6 +527,8 @@ void WM_exit_ext(bContext *C, const bool do_python)
MEM_printmemlist();
}
wm_autosave_delete();
+
+ BLI_temp_dir_session_purge();
}
void WM_exit(bContext *C)
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index a73532680c5..ba454bb1818 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -1086,7 +1086,6 @@ static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg_op)
uiDefBut(block, LABEL, 0, "", 10, 10 - uiSearchBoxHeight(), uiSearchBoxWidth(), uiSearchBoxHeight(), NULL, 0, 0, 0, 0, NULL);
uiPopupBoundsBlock(block, 6, 0, -UI_UNIT_Y); /* move it downwards, mouse over button */
- uiEndBlock(C, block);
wm_event_init_from_window(win, &event);
event.type = EVT_BUT_OPEN;
@@ -1427,7 +1426,6 @@ static uiBlock *wm_block_create_redo(bContext *C, ARegion *ar, void *arg_op)
}
uiPopupBoundsBlock(block, 4, 0, 0);
- uiEndBlock(C, block);
return block;
}
@@ -1462,7 +1460,11 @@ static void dialog_check_cb(bContext *C, void *op_ptr, void *UNUSED(arg))
wmOperator *op = op_ptr;
if (op->type->check) {
if (op->type->check(C, op)) {
- /* refresh */
+ /* check for popup and re-layout buttons */
+ ARegion *ar_menu = CTX_wm_menu(C);
+ if (ar_menu) {
+ ED_region_tag_refresh_ui(ar_menu);
+ }
}
}
}
@@ -1507,7 +1509,6 @@ static uiBlock *wm_block_dialog_create(bContext *C, ARegion *ar, void *userData)
/* center around the mouse */
uiPopupBoundsBlock(block, 4, data->width / -2, data->height / 2);
- uiEndBlock(C, block);
return block;
}
@@ -1530,7 +1531,6 @@ static uiBlock *wm_operator_ui_create(bContext *C, ARegion *ar, void *userData)
uiLayoutOperatorButs(C, layout, op, NULL, 'V', 0);
uiPopupBoundsBlock(block, 4, 0, 0);
- uiEndBlock(C, block);
return block;
}
@@ -1774,6 +1774,7 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
int i;
MenuType *mt = WM_menutype_find("USERPREF_MT_splash", true);
char url[96];
+ const char *version_suffix = NULL;
#ifndef WITH_HEADLESS
extern char datatoc_splash_png[];
@@ -1828,15 +1829,19 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
/* label for 'a' bugfix releases, or 'Release Candidate 1'...
* avoids recreating splash for version updates */
- if (0) {
+ if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "rc")) {
+ version_suffix = "Release Candidate";
+ }
+ else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "release")) {
+ version_suffix = STRINGIFY(BLENDER_VERSION_CHAR);
+ }
+ if (version_suffix != NULL && version_suffix[0]) {
/* placed after the version number in the image,
* placing y is tricky to match baseline */
int x = 260 - (2 * UI_DPI_WINDOW_FAC);
int y = 242 + (4 * UI_DPI_WINDOW_FAC);
int w = 240;
- const char *version_suffix = "Release Candidate";
-
/* hack to have text draw 'text_sel' */
uiBlockSetEmboss(block, UI_EMBOSSN);
but = uiDefBut(block, LABEL, 0, version_suffix, x * U.pixelsize, y * U.pixelsize, w * U.pixelsize, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
@@ -1887,8 +1892,9 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
"http://www.blender.org/foundation/donation-payment/");
uiItemStringO(col, IFACE_("Credits"), ICON_URL, "WM_OT_url_open", "url",
"http://www.blender.org/about/credits/");
- uiItemStringO(col, IFACE_("Release Log"), ICON_URL, "WM_OT_url_open", "url",
- "http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.70");
+ BLI_snprintf(url, sizeof(url), "http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/%d.%d",
+ BLENDER_VERSION / 100, BLENDER_VERSION % 100);
+ uiItemStringO(col, IFACE_("Release Log"), ICON_URL, "WM_OT_url_open", "url", url);
uiItemStringO(col, IFACE_("Manual"), ICON_URL, "WM_OT_url_open", "url",
"http://wiki.blender.org/index.php/Doc:2.6/Manual");
uiItemStringO(col, IFACE_("Blender Website"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org");
@@ -1924,7 +1930,6 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
uiItemL(col, "", ICON_NONE);
uiCenteredBoundsBlock(block, 0);
- uiEndBlock(C, block);
return block;
}
@@ -1966,7 +1971,6 @@ static uiBlock *wm_block_search_menu(bContext *C, ARegion *ar, void *UNUSED(arg_
uiDefBut(block, LABEL, 0, "", 10, 10 - uiSearchBoxHeight(), uiSearchBoxWidth(), uiSearchBoxHeight(), NULL, 0, 0, 0, 0, NULL);
uiPopupBoundsBlock(block, 6, 0, -UI_UNIT_Y); /* move it downwards, mouse over button */
- uiEndBlock(C, block);
wm_event_init_from_window(win, &event);
event.type = EVT_BUT_OPEN;
@@ -2583,7 +2587,7 @@ void WM_recover_last_session(bContext *C, ReportList *reports)
{
char filepath[FILE_MAX];
- BLI_make_file_string("/", filepath, BLI_temporary_dir(), BLENDER_QUIT_FILE);
+ BLI_make_file_string("/", filepath, BLI_temp_dir_base(), BLENDER_QUIT_FILE);
/* if reports==NULL, it's called directly without operator, we add a quick check here */
if (reports || BLI_exists(filepath)) {
G.fileflags |= G_FILE_RECOVER;
@@ -3241,6 +3245,9 @@ static void tweak_gesture_modal(bContext *C, const wmEvent *event)
wmEvent tevent;
wm_event_init_from_window(window, &tevent);
+ /* We want to get coord from start of drag, not from point where it becomes a tweak event, see T40549 */
+ tevent.x = rect->xmin + sx;
+ tevent.y = rect->ymin + sy;
if (gesture->event_type == LEFTMOUSE)
tevent.type = EVT_TWEAK_L;
else if (gesture->event_type == RIGHTMOUSE)
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index b40130379f2..250bb1fb750 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -1465,6 +1465,9 @@ void WM_cursor_warp(wmWindow *win, int x, int y)
win->eventstate->prevx = oldx;
win->eventstate->prevy = oldy;
+
+ win->eventstate->x = oldx;
+ win->eventstate->y = oldy;
}
}
diff --git a/source/blenderplayer/CMakeLists.txt b/source/blenderplayer/CMakeLists.txt
index 887fbe865e6..7ce3fa82f8b 100644
--- a/source/blenderplayer/CMakeLists.txt
+++ b/source/blenderplayer/CMakeLists.txt
@@ -154,6 +154,7 @@ endif()
extern_rangetree
extern_wcwidth
extern_libmv
+ extern_glog
)
if(WITH_MOD_CLOTH_ELTOPO)
diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c
index 20a07418ef3..5c838bd4160 100644
--- a/source/blenderplayer/bad_level_call_stubs/stubs.c
+++ b/source/blenderplayer/bad_level_call_stubs/stubs.c
@@ -45,6 +45,7 @@ struct ARegion;
struct ARegionType;
struct BMEditMesh;
struct Base;
+struct BoundBox;
struct Brush;
struct CSG_FaceIteratorDescriptor;
struct CSG_VertexIteratorDescriptor;
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index e1a4e791311..7df91df23c8 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -178,6 +178,10 @@ if(WITH_BUILDINFO)
list(APPEND SRC
buildinfo.c
)
+
+ # make an object library so can load with it in tests
+ add_library(buildinfoobj OBJECT buildinfo.c)
+ add_dependencies(buildinfoobj buildinfo)
endif()
# message(STATUS "Configuring blender")
@@ -391,13 +395,22 @@ endif()
if(UNIX AND NOT APPLE)
+ install(
+ CODE
+ "
+ execute_process(COMMAND
+ ${CMAKE_SOURCE_DIR}/doc/manpage/blender.1.py
+ ${TARGETDIR}/blender
+ ${TARGETDIR}/blender.1)
+ "
+ )
+
# there are a few differences between portable and system install
if(WITH_INSTALL_PORTABLE)
install(
FILES
${CMAKE_SOURCE_DIR}/release/freedesktop/blender.desktop
${CMAKE_SOURCE_DIR}/release/freedesktop/icons/scalable/apps/blender.svg
- ${CMAKE_SOURCE_DIR}/doc/manpage/blender.1
DESTINATION ${TARGETDIR}
)
@@ -460,7 +473,7 @@ if(UNIX AND NOT APPLE)
DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
)
install(
- FILES ${CMAKE_SOURCE_DIR}/doc/manpage/blender.1
+ FILES ${TARGETDIR}/blender.1
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1
)
install(
@@ -891,241 +904,8 @@ unset(BLENDER_TEXT_FILES)
add_dependencies(blender makesdna)
-get_property(BLENDER_LINK_LIBS GLOBAL PROPERTY BLENDER_LINK_LIBS)
-
-list(APPEND BLENDER_LINK_LIBS
- bf_windowmanager
- bf_render
-)
-
-if(WITH_MOD_FLUID)
- list(APPEND BLENDER_LINK_LIBS bf_intern_elbeem)
-endif()
-
-if(WITH_CYCLES)
- list(APPEND BLENDER_LINK_LIBS
- cycles_render
- cycles_bvh
- cycles_device
- cycles_kernel
- cycles_util
- cycles_subd)
- if(WITH_CYCLES_OSL)
- list(APPEND BLENDER_LINK_LIBS cycles_kernel_osl)
- endif()
-endif()
-
-#if(UNIX)
- # Sort libraries
- set(BLENDER_SORTED_LIBS
- bf_windowmanager
-
- bf_editor_space_api
- bf_editor_space_action
- bf_editor_space_buttons
- bf_editor_space_console
- bf_editor_space_file
- bf_editor_space_graph
- bf_editor_space_image
- bf_editor_space_info
- bf_editor_space_logic
- bf_editor_space_nla
- bf_editor_space_node
- bf_editor_space_outliner
- bf_editor_space_script
- bf_editor_space_sequencer
- bf_editor_space_text
- bf_editor_space_time
- bf_editor_space_userpref
- bf_editor_space_view3d
- bf_editor_space_clip
-
- bf_editor_transform
- bf_editor_util
- bf_editor_uvedit
- bf_editor_curve
- bf_editor_gpencil
- bf_editor_interface
- bf_editor_mesh
- bf_editor_metaball
- bf_editor_object
- bf_editor_armature
- bf_editor_physics
- bf_editor_render
- bf_editor_screen
- bf_editor_sculpt_paint
- bf_editor_sound
- bf_editor_animation
- bf_editor_datafiles
- bf_editor_mask
- bf_editor_io
-
- bf_render
- bf_python
- bf_python_ext
- bf_python_mathutils
- bf_python_bmesh
- bf_freestyle
- bf_ikplugin
- bf_modifiers
- bf_bmesh
- bf_blenkernel
- bf_nodes
- bf_gpu
- bf_blenloader
- bf_imbuf
- bf_blenlib
- bf_intern_ghost
- bf_intern_string
- bf_avi
- bf_imbuf_cineon
- bf_imbuf_openexr
- bf_imbuf_openimageio
- bf_imbuf_dds
- bf_collada
- bf_intern_elbeem
- bf_intern_memutil
- bf_intern_guardedalloc
- bf_intern_ctr
- bf_intern_utfconv
- ge_blen_routines
- ge_converter
- ge_phys_dummy
- ge_phys_bullet
- bf_intern_smoke
- extern_minilzo
- extern_lzma
- extern_colamd
- ge_logic_ketsji
- extern_recastnavigation
- ge_logic
- ge_rasterizer
- ge_oglrasterizer
- ge_logic_expressions
- ge_scenegraph
- ge_logic_network
- ge_logic_ngnetwork
- ge_logic_loopbacknetwork
- bf_intern_moto
- extern_openjpeg
- extern_redcode
- ge_videotex
- bf_rna
- bf_dna
- bf_blenfont
- bf_intern_audaspace
- bf_intern_mikktspace
- bf_intern_dualcon
- bf_intern_cycles
- cycles_render
- cycles_bvh
- cycles_device
- cycles_kernel
- cycles_util
- cycles_subd
- bf_intern_raskter
- bf_intern_opencolorio
- extern_rangetree
- extern_wcwidth
- extern_libmv
- )
-
- if(WITH_COMPOSITOR)
- # added for opencl compositor
- list_insert_before(BLENDER_SORTED_LIBS "bf_blenkernel" "bf_compositor")
- list_insert_after(BLENDER_SORTED_LIBS "bf_compositor" "bf_intern_opencl")
- endif()
-
- if(WITH_LIBMV)
- list(APPEND BLENDER_SORTED_LIBS extern_ceres)
- endif()
-
- if(WITH_MOD_CLOTH_ELTOPO)
- list(APPEND BLENDER_SORTED_LIBS extern_eltopo)
- endif()
-
- if(NOT WITH_SYSTEM_GLEW)
- list(APPEND BLENDER_SORTED_LIBS ${BLENDER_GLEW_LIBRARIES})
- endif()
-
- if(WITH_BINRELOC)
- list(APPEND BLENDER_SORTED_LIBS extern_binreloc)
- endif()
-
- if(WITH_CXX_GUARDEDALLOC)
- list(APPEND BLENDER_SORTED_LIBS bf_intern_guardedalloc_cpp)
- endif()
-
- if(WITH_IK_SOLVER)
- list_insert_after(BLENDER_SORTED_LIBS "bf_intern_elbeem" "bf_intern_iksolver")
- endif()
-
- if(WITH_IK_ITASC)
- list(APPEND BLENDER_SORTED_LIBS bf_intern_itasc)
- endif()
-
- if(WITH_CODEC_QUICKTIME)
- list(APPEND BLENDER_SORTED_LIBS bf_quicktime)
- endif()
-
- if(WITH_INPUT_NDOF)
- list(APPEND BLENDER_SORTED_LIBS bf_intern_ghostndof3dconnexion)
- endif()
-
- if(WITH_MOD_BOOLEAN)
- list(APPEND BLENDER_SORTED_LIBS extern_carve)
- endif()
-
- if(WITH_GHOST_XDND)
- list(APPEND BLENDER_SORTED_LIBS extern_xdnd)
- endif()
-
- if(WITH_CYCLES_OSL)
- list_insert_after(BLENDER_SORTED_LIBS "cycles_kernel" "cycles_kernel_osl")
- endif()
-
- if(WITH_INTERNATIONAL)
- list(APPEND BLENDER_SORTED_LIBS bf_intern_locale)
- endif()
-
- if(WITH_OPENNL)
- list_insert_after(BLENDER_SORTED_LIBS "bf_render" "bf_intern_opennl")
- endif()
-
- if(WITH_BULLET)
- list_insert_after(BLENDER_SORTED_LIBS "bf_blenkernel" "bf_intern_rigidbody")
- endif()
-
- if(WITH_BULLET AND NOT WITH_SYSTEM_BULLET)
- list_insert_after(BLENDER_SORTED_LIBS "ge_logic_ngnetwork" "extern_bullet")
- endif()
-
- foreach(SORTLIB ${BLENDER_SORTED_LIBS})
- set(REMLIB ${SORTLIB})
- foreach(SEARCHLIB ${BLENDER_LINK_LIBS})
- if(${SEARCHLIB} STREQUAL ${SORTLIB})
- set(REMLIB "")
- endif()
- endforeach()
- if(REMLIB)
- # message(STATUS "Removing library ${REMLIB} from blender linking because: not configured")
- list(APPEND REM_MSG ${REMLIB})
- list(REMOVE_ITEM BLENDER_SORTED_LIBS ${REMLIB})
- endif()
- endforeach()
- if(REM_MSG)
- list(SORT REM_MSG)
- message(STATUS "Blender Skipping: (${REM_MSG})")
- endif()
- target_link_libraries(blender ${BLENDER_SORTED_LIBS})
-
- unset(SEARCHLIB)
- unset(SORTLIB)
- unset(REMLIB)
- unset(REM_MSG)
+setup_blender_sorted_libs()
-#else()
-# target_link_libraries(blender ${BLENDER_LINK_LIBS})
-#endif()
+target_link_libraries(blender ${BLENDER_SORTED_LIBS})
setup_liblinks(blender)
diff --git a/source/creator/creator.c b/source/creator/creator.c
index 0dad2fd6b75..70e6aba8274 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -535,7 +535,7 @@ static void blender_crash_handler_backtrace(FILE *fp)
SymInitialize(process, NULL, true);
nframes = CaptureStackBackTrace(0, SIZE, stack, NULL);
- symbolinfo = MEM_callocN(sizeof(SYMBOL_INFO) + MAXSYMBOL * sizeof( char ), "crash Symbol table");
+ symbolinfo = MEM_callocN(sizeof(SYMBOL_INFO) + MAXSYMBOL * sizeof(char), "crash Symbol table");
symbolinfo->MaxNameLen = MAXSYMBOL - 1;
symbolinfo->SizeOfStruct = sizeof(SYMBOL_INFO);
@@ -566,7 +566,7 @@ static void blender_crash_handler(int signum)
char fname[FILE_MAX];
if (!G.main->name[0]) {
- BLI_make_file_string("/", fname, BLI_temporary_dir(), "crash.blend");
+ BLI_make_file_string("/", fname, BLI_temp_dir_base(), "crash.blend");
}
else {
BLI_strncpy(fname, G.main->name, sizeof(fname));
@@ -587,10 +587,10 @@ static void blender_crash_handler(int signum)
char fname[FILE_MAX];
if (!G.main->name[0]) {
- BLI_join_dirfile(fname, sizeof(fname), BLI_temporary_dir(), "blender.crash.txt");
+ BLI_join_dirfile(fname, sizeof(fname), BLI_temp_dir_base(), "blender.crash.txt");
}
else {
- BLI_join_dirfile(fname, sizeof(fname), BLI_temporary_dir(), BLI_path_basename(G.main->name));
+ BLI_join_dirfile(fname, sizeof(fname), BLI_temp_dir_base(), BLI_path_basename(G.main->name));
BLI_replace_extension(fname, sizeof(fname), ".crash.txt");
}
@@ -621,6 +621,8 @@ static void blender_crash_handler(int signum)
fclose(fp);
}
+ /* Delete content of temp dir! */
+ BLI_temp_dir_session_purge();
/* really crash */
signal(signum, SIG_DFL);
@@ -999,6 +1001,7 @@ static int render_frame(int argc, const char **argv, void *data)
break;
}
+ BLI_begin_threaded_malloc();
BKE_reports_init(&reports, RPT_PRINT);
frame = CLAMPIS(frame, MINAFRAME, MAXFRAME);
@@ -1006,6 +1009,7 @@ static int render_frame(int argc, const char **argv, void *data)
RE_SetReports(re, &reports);
RE_BlenderAnim(re, bmain, scene, NULL, scene->lay, frame, frame, scene->r.frame_step);
RE_SetReports(re, NULL);
+ BLI_end_threaded_malloc();
return 1;
}
else {
@@ -1027,10 +1031,12 @@ static int render_animation(int UNUSED(argc), const char **UNUSED(argv), void *d
Main *bmain = CTX_data_main(C);
Render *re = RE_NewRender(scene->id.name);
ReportList reports;
+ BLI_begin_threaded_malloc();
BKE_reports_init(&reports, RPT_PRINT);
RE_SetReports(re, &reports);
RE_BlenderAnim(re, bmain, scene, NULL, scene->lay, scene->r.sfra, scene->r.efra, scene->r.frame_step);
RE_SetReports(re, NULL);
+ BLI_end_threaded_malloc();
}
else {
printf("\nError: no blend loaded. cannot use '-a'.\n");
@@ -1666,7 +1672,7 @@ int main(
/* this is properly initialized with user defs, but this is default */
/* call after loading the startup.blend so we can read U.tempdir */
- BLI_init_temporary_dir(U.tempdir);
+ BLI_temp_dir_init(U.tempdir);
}
else {
#ifndef WITH_PYTHON_MODULE
@@ -1676,7 +1682,7 @@ int main(
WM_init(C, argc, (const char **)argv);
/* don't use user preferences temp dir */
- BLI_init_temporary_dir(NULL);
+ BLI_temp_dir_init(NULL);
}
#ifdef WITH_PYTHON
/**
diff --git a/source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp b/source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp
index b0ccdd1edce..a7c98eb9dc1 100644
--- a/source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp
+++ b/source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp
@@ -186,6 +186,8 @@ static int BL_KetsjiNextFrame(KX_KetsjiEngine *ketsjiengine, bContext *C, wmWind
return exitrequested;
}
+
+#ifdef WITH_PYTHON
static struct BL_KetsjiNextFrameState {
class KX_KetsjiEngine* ketsjiengine;
struct bContext *C;
@@ -210,6 +212,8 @@ static int BL_KetsjiPyNextFrame(void *state0)
state->mousedevice,
state->draw_letterbox);
}
+#endif
+
extern "C" void StartKetsjiShell(struct bContext *C, struct ARegion *ar, rcti *cam_frame, int always_use_expand_framing)
{
diff --git a/source/gameengine/Converter/KX_ConvertActuators.cpp b/source/gameengine/Converter/KX_ConvertActuators.cpp
index 7d5527d4cbb..45fc11b97d2 100644
--- a/source/gameengine/Converter/KX_ConvertActuators.cpp
+++ b/source/gameengine/Converter/KX_ConvertActuators.cpp
@@ -70,6 +70,7 @@
#include "KX_ParentActuator.h"
#include "KX_SCA_DynamicActuator.h"
#include "KX_SteeringActuator.h"
+#include "KX_MouseActuator.h"
#include "KX_Scene.h"
#include "KX_KetsjiEngine.h"
@@ -1094,6 +1095,50 @@ void BL_ConvertActuators(const char* maggiename,
baseact = tmpstact;
break;
}
+ case ACT_MOUSE:
+ {
+ bMouseActuator* mouAct = (bMouseActuator*) bact->data;
+ int mode = KX_MouseActuator::KX_ACT_MOUSE_NODEF;
+
+ switch (mouAct->type) {
+ case ACT_MOUSE_VISIBILITY:
+ {
+ mode = KX_MouseActuator::KX_ACT_MOUSE_VISIBILITY;
+ break;
+ }
+ case ACT_MOUSE_LOOK:
+ {
+ mode = KX_MouseActuator::KX_ACT_MOUSE_LOOK;
+ break;
+ }
+ }
+
+ bool visible = (mouAct->flag & ACT_MOUSE_VISIBLE) != 0;
+ bool use_axis[2] = {(mouAct->flag & ACT_MOUSE_USE_AXIS_X) != 0, (mouAct->flag & ACT_MOUSE_USE_AXIS_Y) != 0};
+ bool reset[2] = {(mouAct->flag & ACT_MOUSE_RESET_X) != 0, (mouAct->flag & ACT_MOUSE_RESET_Y) != 0};
+ bool local[2] = {(mouAct->flag & ACT_MOUSE_LOCAL_X) != 0, (mouAct->flag & ACT_MOUSE_LOCAL_Y) != 0};
+
+ SCA_MouseManager* eventmgr = (SCA_MouseManager*) logicmgr->FindEventManager(SCA_EventManager::MOUSE_EVENTMGR);
+ if (eventmgr) {
+ KX_MouseActuator* tmpbaseact = new KX_MouseActuator(gameobj,
+ ketsjiEngine,
+ eventmgr,
+ mode,
+ visible,
+ use_axis,
+ mouAct->threshold,
+ reset,
+ mouAct->object_axis,
+ local,
+ mouAct->sensitivity,
+ mouAct->limit_x,
+ mouAct->limit_y);
+ baseact = tmpbaseact;
+ } else {
+ //cout << "\n Couldn't find mouse event manager..."; - should throw an error here...
+ }
+ break;
+ }
default:
; /* generate some error */
}
diff --git a/source/gameengine/Converter/KX_ConvertSensors.cpp b/source/gameengine/Converter/KX_ConvertSensors.cpp
index b3c6f6ddd24..93a5ee11366 100644
--- a/source/gameengine/Converter/KX_ConvertSensors.cpp
+++ b/source/gameengine/Converter/KX_ConvertSensors.cpp
@@ -377,6 +377,12 @@ void BL_ConvertSensors(struct Object* blenderobject,
propchecktype = SCA_PropertySensor::KX_PROPSENSOR_EXPRESSION;
/* error */
break;
+ case SENS_PROP_LESSTHAN:
+ propchecktype = SCA_PropertySensor::KX_PROPSENSOR_LESSTHAN;
+ break;
+ case SENS_PROP_GREATERTHAN:
+ propchecktype = SCA_PropertySensor::KX_PROPSENSOR_GREATERTHAN;
+ break;
default:
; /* error */
}
diff --git a/source/gameengine/Expressions/BoolValue.cpp b/source/gameengine/Expressions/BoolValue.cpp
index ee913877c96..9ff53395056 100644
--- a/source/gameengine/Expressions/BoolValue.cpp
+++ b/source/gameengine/Expressions/BoolValue.cpp
@@ -186,6 +186,13 @@ double CBoolValue::GetNumber()
+int CBoolValue::GetValueType()
+{
+ return VALUE_BOOL_TYPE;
+}
+
+
+
const STR_String& CBoolValue::GetText()
{
return m_bool ? sTrueString : sFalseString;
diff --git a/source/gameengine/Expressions/BoolValue.h b/source/gameengine/Expressions/BoolValue.h
index b88c839a58e..161d6112f68 100644
--- a/source/gameengine/Expressions/BoolValue.h
+++ b/source/gameengine/Expressions/BoolValue.h
@@ -41,6 +41,7 @@ public:
virtual const STR_String& GetText();
virtual double GetNumber();
+ virtual int GetValueType();
bool GetBool();
virtual void SetValue(CValue* newval);
diff --git a/source/gameengine/Expressions/EmptyValue.cpp b/source/gameengine/Expressions/EmptyValue.cpp
index 7f3af9f649d..f8e72181ed7 100644
--- a/source/gameengine/Expressions/EmptyValue.cpp
+++ b/source/gameengine/Expressions/EmptyValue.cpp
@@ -82,6 +82,13 @@ double CEmptyValue::GetNumber()
+int CEmptyValue::GetValueType()
+{
+ return VALUE_EMPTY_TYPE;
+}
+
+
+
CListValue* CEmptyValue::GetPolySoup()
{
CListValue* soup = new CListValue();
diff --git a/source/gameengine/Expressions/EmptyValue.h b/source/gameengine/Expressions/EmptyValue.h
index 8eccb97e0f5..88ef206f0f0 100644
--- a/source/gameengine/Expressions/EmptyValue.h
+++ b/source/gameengine/Expressions/EmptyValue.h
@@ -32,6 +32,7 @@ public:
virtual const STR_String & GetText();
virtual double GetNumber();
+ virtual int GetValueType();
CListValue* GetPolySoup();
virtual double* GetVector3(bool bGetTransformedVec=false);
bool IsInside(CValue* testpoint,bool bBorderInclude=true);
diff --git a/source/gameengine/Expressions/ErrorValue.cpp b/source/gameengine/Expressions/ErrorValue.cpp
index ba9c52be6c7..46e09b9073f 100644
--- a/source/gameengine/Expressions/ErrorValue.cpp
+++ b/source/gameengine/Expressions/ErrorValue.cpp
@@ -107,6 +107,13 @@ double CErrorValue::GetNumber()
+int CErrorValue::GetValueType()
+{
+ return VALUE_ERROR_TYPE;
+}
+
+
+
const STR_String & CErrorValue::GetText()
{
return m_strErrorText;
diff --git a/source/gameengine/Expressions/ErrorValue.h b/source/gameengine/Expressions/ErrorValue.h
index 0095528254e..61c72151f40 100644
--- a/source/gameengine/Expressions/ErrorValue.h
+++ b/source/gameengine/Expressions/ErrorValue.h
@@ -27,6 +27,7 @@ class CErrorValue : public CPropValue
public:
virtual const STR_String & GetText();
virtual double GetNumber();
+ virtual int GetValueType();
CErrorValue();
CErrorValue(const char *errmsg);
virtual ~CErrorValue();
diff --git a/source/gameengine/Expressions/FloatValue.cpp b/source/gameengine/Expressions/FloatValue.cpp
index 0f468e328ed..4d6f3f467eb 100644
--- a/source/gameengine/Expressions/FloatValue.cpp
+++ b/source/gameengine/Expressions/FloatValue.cpp
@@ -285,6 +285,13 @@ double CFloatValue::GetNumber()
+int CFloatValue::GetValueType()
+{
+ return VALUE_FLOAT_TYPE;
+}
+
+
+
void CFloatValue::SetValue(CValue* newval)
{
m_float = (float)newval->GetNumber();
diff --git a/source/gameengine/Expressions/FloatValue.h b/source/gameengine/Expressions/FloatValue.h
index bc6a2d052d4..379c3e951bc 100644
--- a/source/gameengine/Expressions/FloatValue.h
+++ b/source/gameengine/Expressions/FloatValue.h
@@ -33,6 +33,7 @@ public:
void Configure(CValue* menuvalue);
virtual double GetNumber();
+ virtual int GetValueType();
virtual void SetValue(CValue* newval);
float GetFloat();
void SetFloat(float fl);
diff --git a/source/gameengine/Expressions/IntValue.cpp b/source/gameengine/Expressions/IntValue.cpp
index fa4c9ad8ac9..a2d055974c3 100644
--- a/source/gameengine/Expressions/IntValue.cpp
+++ b/source/gameengine/Expressions/IntValue.cpp
@@ -15,6 +15,8 @@
*
*/
+#include <stdio.h>
+
#include "IntValue.h"
#include "ErrorValue.h"
#include "FloatValue.h"
@@ -298,6 +300,13 @@ double CIntValue::GetNumber()
+int CIntValue::GetValueType()
+{
+ return VALUE_INT_TYPE;
+}
+
+
+
const STR_String & CIntValue::GetText()
{
if (!m_pstrRep)
diff --git a/source/gameengine/Expressions/IntValue.h b/source/gameengine/Expressions/IntValue.h
index 8411b09693c..6da975f4f7f 100644
--- a/source/gameengine/Expressions/IntValue.h
+++ b/source/gameengine/Expressions/IntValue.h
@@ -31,6 +31,7 @@ class CIntValue : public CPropValue
public:
virtual const STR_String& GetText();
virtual double GetNumber();
+ virtual int GetValueType();
cInt GetInt();
CIntValue();
diff --git a/source/gameengine/Expressions/ListValue.cpp b/source/gameengine/Expressions/ListValue.cpp
index 1f12a9b78e1..75e3b490505 100644
--- a/source/gameengine/Expressions/ListValue.cpp
+++ b/source/gameengine/Expressions/ListValue.cpp
@@ -250,6 +250,13 @@ double CListValue::GetNumber()
+int CListValue::GetValueType()
+{
+ return VALUE_LIST_TYPE;
+}
+
+
+
void CListValue::SetModified(bool bModified)
{
CValue::SetModified(bModified);
diff --git a/source/gameengine/Expressions/ListValue.h b/source/gameengine/Expressions/ListValue.h
index 5240c54ae4e..bb188179836 100644
--- a/source/gameengine/Expressions/ListValue.h
+++ b/source/gameengine/Expressions/ListValue.h
@@ -40,6 +40,7 @@ public:
VALUE_OPERATOR op,
CValue* val);
virtual double GetNumber();
+ virtual int GetValueType();
virtual CValue* GetReplica();
public:
diff --git a/source/gameengine/Expressions/StringValue.cpp b/source/gameengine/Expressions/StringValue.cpp
index 166125bc906..098949c9d7b 100644
--- a/source/gameengine/Expressions/StringValue.cpp
+++ b/source/gameengine/Expressions/StringValue.cpp
@@ -120,6 +120,13 @@ double CStringValue::GetNumber()
+int CStringValue::GetValueType()
+{
+ return VALUE_STRING_TYPE;
+}
+
+
+
const STR_String & CStringValue::GetText()
{
return m_strString;
diff --git a/source/gameengine/Expressions/StringValue.h b/source/gameengine/Expressions/StringValue.h
index 22d433455ec..cb60600ad88 100644
--- a/source/gameengine/Expressions/StringValue.h
+++ b/source/gameengine/Expressions/StringValue.h
@@ -36,6 +36,7 @@ public:
virtual bool IsEqual(const STR_String & other);
virtual const STR_String & GetText();
virtual double GetNumber();
+ virtual int GetValueType();
virtual CValue* Calc(VALUE_OPERATOR op, CValue *val);
virtual CValue* CalcFinal(VALUE_DATA_TYPE dtype, VALUE_OPERATOR op, CValue *val);
diff --git a/source/gameengine/Expressions/Value.cpp b/source/gameengine/Expressions/Value.cpp
index e5c4001de4c..1ced71e66a4 100644
--- a/source/gameengine/Expressions/Value.cpp
+++ b/source/gameengine/Expressions/Value.cpp
@@ -494,6 +494,15 @@ void CValue::ProcessReplica() /* was AddDataToReplica in 2.48 */
}
}
+
+
+int CValue::GetValueType()
+{
+ return VALUE_NO_TYPE;
+}
+
+
+
CValue* CValue::FindIdentifier(const STR_String& identifiername)
{
diff --git a/source/gameengine/Expressions/Value.h b/source/gameengine/Expressions/Value.h
index db7d69a638f..7f6ce9aa703 100644
--- a/source/gameengine/Expressions/Value.h
+++ b/source/gameengine/Expressions/Value.h
@@ -82,11 +82,9 @@ enum VALUE_DATA_TYPE {
VALUE_BOOL_TYPE,
VALUE_ERROR_TYPE,
VALUE_EMPTY_TYPE,
- VALUE_SOLID_TYPE,
- VALUE_COMBISOLID_TYPE,
+ VALUE_LIST_TYPE,
+ VALUE_VOID_TYPE,
VALUE_VECTOR_TYPE,
- VALUE_MENU_TYPE,
- VALUE_ACTOR_TYPE,
VALUE_MAX_TYPE //only here to provide number of types
};
@@ -311,6 +309,7 @@ public:
virtual const STR_String & GetText() = 0;
virtual double GetNumber() = 0;
+ virtual int GetValueType(); // Get Prop value type
double* ZeroVector() { return m_sZeroVec; }
virtual double* GetVector3(bool bGetTransformedVec = false);
@@ -323,7 +322,6 @@ public:
virtual void ProcessReplica();
//virtual CValue* Copy() = 0;
-
STR_String op2str(VALUE_OPERATOR op);
// setting / getting flags
diff --git a/source/gameengine/Expressions/VectorValue.cpp b/source/gameengine/Expressions/VectorValue.cpp
index a0e1b616d5e..6931ba2aa76 100644
--- a/source/gameengine/Expressions/VectorValue.cpp
+++ b/source/gameengine/Expressions/VectorValue.cpp
@@ -162,6 +162,14 @@ double CVectorValue::GetNumber()
}
+
+int CVectorValue::GetValueType()
+{
+ return VALUE_VECTOR_TYPE;
+}
+
+
+
double* CVectorValue::GetVector3(bool bGetTransformedVec)
{
if (bGetTransformedVec)
diff --git a/source/gameengine/Expressions/VectorValue.h b/source/gameengine/Expressions/VectorValue.h
index 69f2d4b54ae..717fa68f6dc 100644
--- a/source/gameengine/Expressions/VectorValue.h
+++ b/source/gameengine/Expressions/VectorValue.h
@@ -37,6 +37,7 @@ public:
void Configure(CValue* menuvalue);
virtual double* GetVector3(bool bGetTransformedVec=false);
virtual double GetNumber();
+ virtual int GetValueType();
CValue* Calc(VALUE_OPERATOR op, CValue *val) {
return val->CalcFinal(VALUE_VECTOR_TYPE, op, this);
diff --git a/source/gameengine/Expressions/VoidValue.h b/source/gameengine/Expressions/VoidValue.h
index 832f75c9c9f..4f74910dd8a 100644
--- a/source/gameengine/Expressions/VoidValue.h
+++ b/source/gameengine/Expressions/VoidValue.h
@@ -59,6 +59,7 @@ public:
/// Value -> String or number
virtual const STR_String & GetText(); /* Get string description of void value (unimplemented) */
virtual double GetNumber() { return -1; }
+ virtual int GetValueType() { return VALUE_VOID_TYPE; }
/// Value calculation
virtual CValue* Calc(VALUE_OPERATOR op, CValue *val);
diff --git a/source/gameengine/GameLogic/SCA_IActuator.h b/source/gameengine/GameLogic/SCA_IActuator.h
index aed49bc1822..8c22fb39c80 100644
--- a/source/gameengine/GameLogic/SCA_IActuator.h
+++ b/source/gameengine/GameLogic/SCA_IActuator.h
@@ -89,6 +89,7 @@ public:
KX_ACT_STATE,
KX_ACT_ARMATURE,
KX_ACT_STEERING,
+ KX_ACT_MOUSE,
};
SCA_IActuator(SCA_IObject* gameobj, KX_ACTUATOR_TYPE type);
diff --git a/source/gameengine/GameLogic/SCA_PropertySensor.cpp b/source/gameengine/GameLogic/SCA_PropertySensor.cpp
index ad57e529297..6f34f8710c1 100644
--- a/source/gameengine/GameLogic/SCA_PropertySensor.cpp
+++ b/source/gameengine/GameLogic/SCA_PropertySensor.cpp
@@ -133,6 +133,7 @@ bool SCA_PropertySensor::CheckPropertyCondition()
{
case KX_PROPSENSOR_NOTEQUAL:
reverse = true;
+ /* fall-through */
case KX_PROPSENSOR_EQUAL:
{
CValue* orgprop = GetParent()->FindIdentifier(m_checkpropname);
@@ -150,7 +151,7 @@ bool SCA_PropertySensor::CheckPropertyCondition()
/* Patch: floating point values cant use strings usefully since you can have "0.0" == "0.0000"
* this could be made into a generic Value class function for comparing values with a string.
*/
- if (result==false && dynamic_cast<CFloatValue *>(orgprop) != NULL) {
+ if (result==false && (orgprop->GetValueType() == VALUE_FLOAT_TYPE)) {
float f;
if (sscanf(m_checkpropval.ReadPtr(), "%f", &f) == 1) {
result = (f == ((CFloatValue *)orgprop)->GetFloat());
@@ -198,11 +199,11 @@ bool SCA_PropertySensor::CheckPropertyCondition()
const float max = m_checkpropmaxval.ToFloat();
float val;
- if (dynamic_cast<CStringValue *>(orgprop) == NULL) {
- val = orgprop->GetNumber();
+ if (orgprop->GetValueType() == VALUE_STRING_TYPE){
+ val = orgprop->GetText().ToFloat();
}
else {
- val = orgprop->GetText().ToFloat();
+ val = orgprop->GetNumber();
}
result = (min <= val) && (val <= max);
@@ -228,6 +229,36 @@ bool SCA_PropertySensor::CheckPropertyCondition()
//cout << " \nSens:Prop:changed!"; /* need implementation here!!! */
break;
}
+ case KX_PROPSENSOR_LESSTHAN:
+ reverse = true;
+ /* fall-through */
+ case KX_PROPSENSOR_GREATERTHAN:
+ {
+ CValue* orgprop = GetParent()->FindIdentifier(m_checkpropname);
+ if (!orgprop->IsError())
+ {
+ const float ref = m_checkpropval.ToFloat();
+ float val;
+
+ if (orgprop->GetValueType() == VALUE_STRING_TYPE){
+ val = orgprop->GetText().ToFloat();
+ }
+ else {
+ val = orgprop->GetNumber();
+ }
+
+ if (reverse) {
+ result = val < ref;
+ }
+ else {
+ result = val > ref;
+ }
+
+ }
+ orgprop->Release();
+
+ break;
+ }
default:
; /* error */
}
diff --git a/source/gameengine/GameLogic/SCA_PropertySensor.h b/source/gameengine/GameLogic/SCA_PropertySensor.h
index fee03dcf4c0..f9cfb5fb1d2 100644
--- a/source/gameengine/GameLogic/SCA_PropertySensor.h
+++ b/source/gameengine/GameLogic/SCA_PropertySensor.h
@@ -57,6 +57,8 @@ public:
KX_PROPSENSOR_INTERVAL,
KX_PROPSENSOR_CHANGED,
KX_PROPSENSOR_EXPRESSION,
+ KX_PROPSENSOR_LESSTHAN,
+ KX_PROPSENSOR_GREATERTHAN,
KX_PROPSENSOR_MAX
};
diff --git a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp
index 51de370eb2e..e880861ea95 100644
--- a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp
+++ b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp
@@ -430,7 +430,7 @@ int main(int argc, char** argv)
#endif /* __alpha__ */
#endif /* __linux__ */
BLI_init_program_path(argv[0]);
- BLI_init_temporary_dir(NULL);
+ BLI_temp_dir_init(NULL);
// We don't use threads directly in the BGE, but we need to call this so things like
// freeing up GPU_Textures works correctly.
@@ -1141,5 +1141,7 @@ int main(int argc, char** argv)
MEM_printmemlist();
}
+ BLI_temp_dir_session_purge();
+
return error ? -1 : 0;
}
diff --git a/source/gameengine/Ketsji/CMakeLists.txt b/source/gameengine/Ketsji/CMakeLists.txt
index 98ec18c1d48..44532e5d737 100644
--- a/source/gameengine/Ketsji/CMakeLists.txt
+++ b/source/gameengine/Ketsji/CMakeLists.txt
@@ -92,6 +92,7 @@ set(SRC
KX_MaterialIpoController.cpp
KX_MeshProxy.cpp
KX_MotionState.cpp
+ KX_MouseActuator.cpp
KX_MouseFocusSensor.cpp
KX_NavMeshObject.cpp
KX_NearSensor.cpp
@@ -170,6 +171,7 @@ set(SRC
KX_MaterialIpoController.h
KX_MeshProxy.h
KX_MotionState.h
+ KX_MouseActuator.h
KX_MouseFocusSensor.h
KX_NavMeshObject.h
KX_NearSensor.h
diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
index dc81bae1f0c..7d7e15a5141 100644
--- a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
+++ b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
@@ -514,13 +514,13 @@ void KX_KetsjiEngine::EndFrame()
RenderDebugProperties();
}
- double tottime = m_logger->GetAverage(), time;
+ double tottime = m_logger->GetAverage();
if (tottime < 1e-6)
tottime = 1e-6;
#ifdef WITH_PYTHON
for (int i = tc_first; i < tc_numCategories; ++i) {
- time = m_logger->GetAverage((KX_TimeCategory)i);
+ double time = m_logger->GetAverage((KX_TimeCategory)i);
PyObject *val = PyTuple_New(2);
PyTuple_SetItem(val, 0, PyFloat_FromDouble(time*1000.f));
PyTuple_SetItem(val, 1, PyFloat_FromDouble(time/tottime * 100.f));
diff --git a/source/gameengine/Ketsji/KX_MouseActuator.cpp b/source/gameengine/Ketsji/KX_MouseActuator.cpp
new file mode 100644
index 00000000000..3d74bd7c98a
--- /dev/null
+++ b/source/gameengine/Ketsji/KX_MouseActuator.cpp
@@ -0,0 +1,525 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Geoffrey Gollmer, Jorge Bernal
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "KX_MouseActuator.h"
+#include "KX_KetsjiEngine.h"
+#include "SCA_MouseManager.h"
+#include "SCA_IInputDevice.h"
+#include "RAS_ICanvas.h"
+#include "KX_GameObject.h"
+#include "MT_Vector3.h"
+#include "MT_Scalar.h"
+#include "MT_assert.h"
+#include "limits.h"
+
+#include "BLI_math.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* ------------------------------------------------------------------------- */
+/* Native functions */
+/* ------------------------------------------------------------------------- */
+
+KX_MouseActuator::KX_MouseActuator(
+ SCA_IObject* gameobj,
+
+ KX_KetsjiEngine* ketsjiEngine,
+ SCA_MouseManager* eventmgr,
+ int acttype,
+ bool visible,
+ bool* use_axis,
+ float* threshold,
+ bool* reset,
+ int* object_axis,
+ bool* local,
+ float* sensitivity,
+ float* limit_x,
+ float* limit_y
+):
+ SCA_IActuator(gameobj, KX_ACT_MOUSE),
+ m_ketsji(ketsjiEngine),
+ m_eventmgr(eventmgr),
+ m_type(acttype),
+ m_visible(visible),
+ m_use_axis_x(use_axis[0]),
+ m_use_axis_y(use_axis[1]),
+ m_reset_x(reset[0]),
+ m_reset_y(reset[1]),
+ m_local_x(local[0]),
+ m_local_y(local[1])
+{
+ m_canvas = m_ketsji->GetCanvas();
+ m_oldposition[0] = m_oldposition[1] = -1.f;
+ m_limit_x[0] = limit_x[0];
+ m_limit_x[1] = limit_x[1];
+ m_limit_y[0] = limit_y[0];
+ m_limit_y[1] = limit_y[1];
+ m_threshold[0] = threshold[0];
+ m_threshold[1] = threshold[1];
+ m_object_axis[0] = object_axis[0];
+ m_object_axis[1] = object_axis[1];
+ m_sensitivity[0] = sensitivity[0];
+ m_sensitivity[1] = sensitivity[1];
+ m_angle[0] = 0.f;
+ m_angle[1] = 0.f;
+}
+
+KX_MouseActuator::~KX_MouseActuator()
+{
+}
+
+bool KX_MouseActuator::Update()
+{
+ bool result = false;
+
+ bool bNegativeEvent = IsNegativeEvent();
+ RemoveAllEvents();
+
+ if (bNegativeEvent)
+ return false; // do nothing on negative events
+
+ KX_GameObject *parent = static_cast<KX_GameObject *>(GetParent());
+
+ m_mouse = ((SCA_MouseManager *)m_eventmgr)->GetInputDevice();
+
+ switch (m_type) {
+ case KX_ACT_MOUSE_VISIBILITY:
+ {
+ if (m_visible) {
+ if (m_canvas) {
+ m_canvas->SetMouseState(RAS_ICanvas::MOUSE_NORMAL);
+ }
+ }
+ else {
+ if (m_canvas) {
+ m_canvas->SetMouseState(RAS_ICanvas::MOUSE_INVISIBLE);
+ }
+ }
+ break;
+ }
+ case KX_ACT_MOUSE_LOOK:
+ {
+ if (m_mouse) {
+
+ float position[2];
+ float movement[2];
+ MT_Vector3 rotation;
+ float setposition[2] = {0.0};
+
+ getMousePosition(position);
+
+ movement[0] = position[0];
+ movement[1] = position[1];
+
+ //preventing initial skipping.
+ if ((m_oldposition[0] <= -0.9) && (m_oldposition[1] <= -0.9)) {
+
+ if (m_reset_x) {
+ m_oldposition[0] = 0.5;
+ }
+ else {
+ m_oldposition[0] = position[0];
+ }
+
+ if (m_reset_y) {
+ m_oldposition[1] = 0.5;
+ }
+ else {
+ m_oldposition[1] = position[1];
+ }
+ setMousePosition(m_oldposition[0], m_oldposition[1]);
+ break;
+ }
+
+ //Calculating X axis.
+ if (m_use_axis_x) {
+
+ if (m_reset_x) {
+ setposition[0] = 0.5;
+ movement[0] -= 0.5;
+ }
+ else {
+ setposition[0] = position[0];
+ movement[0] -= m_oldposition[0];
+ }
+
+ movement[0] *= -1.0;
+
+ /* Don't apply the rotation when width resolution is odd (+ little movement) to
+ avoid undesired drifting or when we are under a certain threshold for mouse
+ movement */
+
+ if (!((m_canvas->GetWidth() % 2 != 0) && MT_abs(movement[0]) < 0.01) &&
+ ((movement[0] > (m_threshold[0] / 10.0)) ||
+ ((movement[0] * (-1.0)) > (m_threshold[0] / 10.0)))) {
+
+ movement[0] *= m_sensitivity[0];
+
+ if ((m_limit_x[0] != 0.0) && ((m_angle[0] + movement[0]) <= m_limit_x[0])) {
+ movement[0] = m_limit_x[0] - m_angle[0];
+ }
+
+ if ((m_limit_x[1] != 0.0) && ((m_angle[0] + movement[0]) >= m_limit_x[1])) {
+ movement[0] = m_limit_x[1] - m_angle[0];
+ }
+
+ m_angle[0] += movement[0];
+
+ switch (m_object_axis[0]) {
+ case KX_ACT_MOUSE_OBJECT_AXIS_X:
+ {
+ rotation = MT_Vector3(movement[0], 0.0, 0.0);
+ break;
+ }
+ case KX_ACT_MOUSE_OBJECT_AXIS_Y:
+ {
+ rotation = MT_Vector3(0.0, movement[0], 0.0);
+ break;
+ }
+ case KX_ACT_MOUSE_OBJECT_AXIS_Z:
+ {
+ rotation = MT_Vector3(0.0, 0.0, movement[0]);
+ break;
+ }
+ default:
+ break;
+ }
+ parent->ApplyRotation(rotation, m_local_x);
+ }
+ }
+
+ //Calculating Y axis.
+ if (m_use_axis_y) {
+
+ if (m_reset_y) {
+ setposition[1] = 0.5;
+ movement[1] -= 0.5;
+ }
+ else {
+ setposition[1] = position[1];
+ movement[1] -= m_oldposition[1];
+ }
+
+ movement[1] *= -1.0;
+
+ /* Don't apply the rotation when height resolution is odd (+ little movement) to
+ avoid undesired drifting or when we are under a certain threshold for mouse
+ movement */
+
+ if (!((m_canvas->GetHeight() % 2 != 0) && MT_abs(movement[1]) < 0.01) &&
+ ((movement[1] > (m_threshold[1] / 10.0)) ||
+ ((movement[1] * (-1.0)) > (m_threshold[1] / 10.0)))) {
+
+ movement[1] *= m_sensitivity[1];
+
+ if ((m_limit_y[0] != 0.0) && ((m_angle[1] + movement[1]) <= m_limit_y[0])) {
+ movement[1] = m_limit_y[0] - m_angle[1];
+ }
+
+ if ((m_limit_y[1] != 0.0) && ((m_angle[1] + movement[1]) >= m_limit_y[1])) {
+ movement[1] = m_limit_y[1] - m_angle[1];
+ }
+
+ m_angle[1] += movement[1];
+
+ switch (m_object_axis[1])
+ {
+ case KX_ACT_MOUSE_OBJECT_AXIS_X:
+ {
+ rotation = MT_Vector3(movement[1], 0.0, 0.0);
+ break;
+ }
+ case KX_ACT_MOUSE_OBJECT_AXIS_Y:
+ {
+ rotation = MT_Vector3(0.0, movement[1], 0.0);
+ break;
+ }
+ case KX_ACT_MOUSE_OBJECT_AXIS_Z:
+ {
+ rotation = MT_Vector3(0.0, 0.0, movement[1]);
+ break;
+ }
+ default:
+ break;
+ }
+ parent->ApplyRotation(rotation, m_local_y);
+ }
+ }
+
+ setMousePosition(setposition[0], setposition[1]);
+
+ m_oldposition[0] = position[0];
+ m_oldposition[1] = position[1];
+
+ }
+ else {
+ //printf("\nNo input device detected for mouse actuator\n");
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return result;
+}
+
+bool KX_MouseActuator::isValid(KX_MouseActuator::KX_ACT_MOUSE_MODE mode)
+{
+ return ((mode > KX_ACT_MOUSE_NODEF) && (mode < KX_ACT_MOUSE_MAX));
+}
+
+
+CValue* KX_MouseActuator::GetReplica()
+{
+ KX_MouseActuator* replica = new KX_MouseActuator(*this);
+
+ replica->ProcessReplica();
+ return replica;
+}
+
+void KX_MouseActuator::ProcessReplica()
+{
+ SCA_IActuator::ProcessReplica();
+}
+
+void KX_MouseActuator::getMousePosition(float* pos)
+{
+ MT_assert(!m_mouse);
+ const SCA_InputEvent & xevent = m_mouse->GetEventValue(SCA_IInputDevice::KX_MOUSEX);
+ const SCA_InputEvent & yevent = m_mouse->GetEventValue(SCA_IInputDevice::KX_MOUSEY);
+
+ pos[0] = m_canvas->GetMouseNormalizedX(xevent.m_eventval);
+ pos[1] = m_canvas->GetMouseNormalizedY(yevent.m_eventval);
+}
+
+void KX_MouseActuator::setMousePosition(float fx, float fy)
+{
+ int x, y;
+
+ x = (int)(fx * m_canvas->GetWidth());
+ y = (int)(fy * m_canvas->GetHeight());
+
+ m_canvas->SetMousePosition(x, y);
+}
+
+#ifndef DISABLE_PYTHON
+
+/* ------------------------------------------------------------------------- */
+/* Python functions */
+/* ------------------------------------------------------------------------- */
+
+/* Integration hooks ------------------------------------------------------- */
+PyTypeObject KX_MouseActuator::Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "KX_MouseActuator",
+ sizeof(PyObjectPlus_Proxy),
+ 0,
+ py_base_dealloc,
+ 0,
+ 0,
+ 0,
+ 0,
+ py_base_repr,
+ 0,0,0,0,0,0,0,0,0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ 0,0,0,0,0,0,0,
+ Methods,
+ 0,
+ 0,
+ &SCA_IActuator::Type,
+ 0,0,0,0,0,0,
+ py_base_new
+};
+
+PyMethodDef KX_MouseActuator::Methods[] = {
+ {"reset", (PyCFunction) KX_MouseActuator::sPyReset, METH_NOARGS,"reset() : undo rotation caused by actuator\n"},
+ {NULL,NULL} //Sentinel
+};
+
+
+
+PyAttributeDef KX_MouseActuator::Attributes[] = {
+ KX_PYATTRIBUTE_BOOL_RW("visible", KX_MouseActuator, m_visible),
+ KX_PYATTRIBUTE_BOOL_RW("use_axis_x", KX_MouseActuator, m_use_axis_x),
+ KX_PYATTRIBUTE_BOOL_RW("use_axis_y", KX_MouseActuator, m_use_axis_y),
+ KX_PYATTRIBUTE_FLOAT_ARRAY_RW("threshold", 0.0, 0.5, KX_MouseActuator, m_threshold, 2),
+ KX_PYATTRIBUTE_BOOL_RW("reset_x", KX_MouseActuator, m_reset_x),
+ KX_PYATTRIBUTE_BOOL_RW("reset_y", KX_MouseActuator, m_reset_y),
+ KX_PYATTRIBUTE_INT_ARRAY_RW("object_axis", 0, 2, 1, KX_MouseActuator, m_object_axis, 2),
+ KX_PYATTRIBUTE_BOOL_RW("local_x", KX_MouseActuator, m_local_x),
+ KX_PYATTRIBUTE_BOOL_RW("local_y", KX_MouseActuator, m_local_y),
+ KX_PYATTRIBUTE_FLOAT_ARRAY_RW("sensitivity", -FLT_MAX, FLT_MAX, KX_MouseActuator, m_sensitivity, 2),
+ KX_PYATTRIBUTE_RW_FUNCTION("limit_x", KX_MouseActuator, pyattr_get_limit_x, pyattr_set_limit_x),
+ KX_PYATTRIBUTE_RW_FUNCTION("limit_y", KX_MouseActuator, pyattr_get_limit_y, pyattr_set_limit_y),
+ KX_PYATTRIBUTE_RW_FUNCTION("angle", KX_MouseActuator, pyattr_get_angle, pyattr_set_angle),
+ { NULL } //Sentinel
+};
+
+PyObject* KX_MouseActuator::pyattr_get_limit_x(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
+{
+ KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
+ return Py_BuildValue("[f,f]", (self->m_limit_x[0] / M_PI * 180.0), (self->m_limit_x[1] / M_PI * 180.0));
+}
+
+int KX_MouseActuator::pyattr_set_limit_x(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
+{
+ PyObject *item1, *item2;
+ KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
+
+ if (!PyList_Check(value))
+ return PY_SET_ATTR_FAIL;
+
+ if (PyList_Size(value) != 2)
+ return PY_SET_ATTR_FAIL;
+
+ item1 = PyList_GetItem(value, 0);
+ item2 = PyList_GetItem(value, 1);
+
+ if (!(PyFloat_Check(item1)) || !(PyFloat_Check(item2))) {
+ return PY_SET_ATTR_FAIL;
+ }
+ else {
+ self->m_limit_x[0] = (PyFloat_AsDouble(item1) * M_PI / 180.0);
+ self->m_limit_x[1] = (PyFloat_AsDouble(item2) * M_PI / 180.0);
+ }
+
+ return PY_SET_ATTR_SUCCESS;
+}
+
+PyObject* KX_MouseActuator::pyattr_get_limit_y(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
+{
+ KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
+ return Py_BuildValue("[f,f]", (self->m_limit_y[0] / M_PI * 180.0), (self->m_limit_y[1] / M_PI * 180.0));
+}
+
+int KX_MouseActuator::pyattr_set_limit_y(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
+{
+ PyObject *item1, *item2;
+ KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
+
+ if (!PyList_Check(value))
+ return PY_SET_ATTR_FAIL;
+
+ if (PyList_Size(value) != 2)
+ return PY_SET_ATTR_FAIL;
+
+ item1 = PyList_GetItem(value, 0);
+ item2 = PyList_GetItem(value, 1);
+
+ if (!(PyFloat_Check(item1)) || !(PyFloat_Check(item2))) {
+ return PY_SET_ATTR_FAIL;
+ }
+ else {
+ self->m_limit_y[0] = (PyFloat_AsDouble(item1) * M_PI / 180.0);
+ self->m_limit_y[1] = (PyFloat_AsDouble(item2) * M_PI / 180.0);
+ }
+
+ return PY_SET_ATTR_SUCCESS;
+}
+
+PyObject* KX_MouseActuator::pyattr_get_angle(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
+{
+ KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
+ return Py_BuildValue("[f,f]", (self->m_angle[0] / M_PI * 180.0), (self->m_angle[1] / M_PI * 180.0));
+}
+
+int KX_MouseActuator::pyattr_set_angle(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
+{
+ PyObject *item1, *item2;
+ KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
+
+ if (!PyList_Check(value))
+ return PY_SET_ATTR_FAIL;
+
+ if (PyList_Size(value) != 2)
+ return PY_SET_ATTR_FAIL;
+
+ item1 = PyList_GetItem(value, 0);
+ item2 = PyList_GetItem(value, 1);
+
+ if (!(PyFloat_Check(item1)) || !(PyFloat_Check(item2))) {
+ return PY_SET_ATTR_FAIL;
+ }
+ else {
+ self->m_angle[0] = (PyFloat_AsDouble(item1) * M_PI / 180.0);
+ self->m_angle[1] = (PyFloat_AsDouble(item2) * M_PI / 180.0);
+ }
+
+ return PY_SET_ATTR_SUCCESS;
+}
+
+PyObject* KX_MouseActuator::PyReset()
+{
+ MT_Vector3 rotation;
+ KX_GameObject *parent = static_cast<KX_GameObject *>(GetParent());
+
+ switch (m_object_axis[0]) {
+ case KX_ACT_MOUSE_OBJECT_AXIS_X:
+ {
+ rotation = MT_Vector3(-1.0 * m_angle[0], 0.0, 0.0);
+ break;
+ }
+ case KX_ACT_MOUSE_OBJECT_AXIS_Y:
+ {
+ rotation = MT_Vector3(0.0, -1.0 * m_angle[0], 0.0);
+ break;
+ }
+ case KX_ACT_MOUSE_OBJECT_AXIS_Z:
+ {
+ rotation = MT_Vector3(0.0, 0.0, -1.0 * m_angle[0]);
+ break;
+ }
+ default:
+ break;
+ }
+ parent->ApplyRotation(rotation, m_local_x);
+
+ switch (m_object_axis[1]) {
+ case KX_ACT_MOUSE_OBJECT_AXIS_X:
+ {
+ rotation = MT_Vector3(-1.0 * m_angle[1], 0.0, 0.0);
+ break;
+ }
+ case KX_ACT_MOUSE_OBJECT_AXIS_Y:
+ {
+ rotation = MT_Vector3(0.0, -1.0 * m_angle[1], 0.0);
+ break;
+ }
+ case KX_ACT_MOUSE_OBJECT_AXIS_Z:
+ {
+ rotation = MT_Vector3(0.0, 0.0, -1.0 * m_angle[1]);
+ break;
+ }
+ default:
+ break;
+ }
+ parent->ApplyRotation(rotation, m_local_y);
+
+ m_angle[0] = 0.0;
+ m_angle[1] = 0.0;
+
+ Py_RETURN_NONE;
+}
+
+#endif
diff --git a/source/gameengine/Ketsji/KX_MouseActuator.h b/source/gameengine/Ketsji/KX_MouseActuator.h
new file mode 100644
index 00000000000..bf90bd21dac
--- /dev/null
+++ b/source/gameengine/Ketsji/KX_MouseActuator.h
@@ -0,0 +1,127 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Geoffrey Gollmer, Jorge Bernal
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __KX_MOUSEACTUATOR
+#define __KX_MOUSEACTUATOR
+
+#include "SCA_IActuator.h"
+
+class KX_KetsjiEngine;
+class SCA_MouseManager;
+class SCA_IInputDevice;
+class RAS_ICanvas;
+
+class KX_MouseActuator : public SCA_IActuator
+{
+ Py_Header
+
+private:
+
+ KX_KetsjiEngine* m_ketsji;
+ SCA_MouseManager* m_eventmgr;
+ SCA_IInputDevice* m_mouse;
+ RAS_ICanvas* m_canvas;
+ int m_type;
+
+ bool m_visible;
+
+ bool m_use_axis_x; /* 0 for calculate axis, 1 for ignore axis */
+ bool m_use_axis_y;
+ float m_threshold[2];
+ bool m_reset_x; /* 0=reset, 1=free */
+ bool m_reset_y;
+ int m_object_axis[2]; /* 0=x, 1=y, 2=z */
+ bool m_local_x; /* 0=local, 1=global*/
+ bool m_local_y;
+ float m_sensitivity[2];
+ float m_limit_x[2];
+ float m_limit_y[2];
+
+ float m_oldposition[2];
+ float m_angle[2];
+
+public:
+
+ enum KX_ACT_MOUSE_OBJECT_AXIS {
+ KX_ACT_MOUSE_OBJECT_AXIS_X = 0,
+ KX_ACT_MOUSE_OBJECT_AXIS_Y,
+ KX_ACT_MOUSE_OBJECT_AXIS_Z
+ };
+
+ enum KX_ACT_MOUSE_MODE {
+ KX_ACT_MOUSE_NODEF = 0,
+ KX_ACT_MOUSE_VISIBILITY,
+ KX_ACT_MOUSE_LOOK,
+ KX_ACT_MOUSE_MAX
+ };
+
+ KX_MouseActuator(
+ SCA_IObject* gameobj,
+ KX_KetsjiEngine* ketsjiEngine,
+ SCA_MouseManager* eventmgr,
+ int acttype,
+ bool visible,
+ bool* use_axis,
+ float* threshold,
+ bool* reset,
+ int* object_axis,
+ bool* local,
+ float* sensitivity,
+ float* limit_x,
+ float* limit_y
+ );
+
+
+ ~KX_MouseActuator();
+
+ CValue* GetReplica();
+ virtual void ProcessReplica();
+
+ virtual bool Update();
+
+ /* check whether this value is valid */
+ bool isValid(KX_ACT_MOUSE_MODE mode);
+
+ virtual void getMousePosition(float*);
+ virtual void setMousePosition(float, float);
+
+ /* --------------------------------------------------------------------- */
+ /* Python interface ---------------------------------------------------- */
+ /* --------------------------------------------------------------------- */
+
+ /* Methods */
+
+ KX_PYMETHOD_DOC_NOARGS(KX_MouseActuator,Reset);
+
+ /* Attributes */
+
+ static PyObject* pyattr_get_limit_x(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
+ static int pyattr_set_limit_x(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
+
+ static PyObject* pyattr_get_limit_y(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
+ static int pyattr_set_limit_y(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
+
+ static PyObject* pyattr_get_angle(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
+ static int pyattr_set_angle(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
+};
+
+#endif //__KX_MOUSEACTUATOR_DOC
diff --git a/source/gameengine/Ketsji/KX_ObjectActuator.cpp b/source/gameengine/Ketsji/KX_ObjectActuator.cpp
index 2f85453dd23..0eec86987be 100644
--- a/source/gameengine/Ketsji/KX_ObjectActuator.cpp
+++ b/source/gameengine/Ketsji/KX_ObjectActuator.cpp
@@ -32,6 +32,7 @@
* \ingroup ketsji
*/
+#include <stdio.h>
#include "KX_ObjectActuator.h"
#include "KX_GameObject.h"
diff --git a/source/gameengine/Ketsji/KX_PythonInit.cpp b/source/gameengine/Ketsji/KX_PythonInit.cpp
index fa20ea987f3..64705c60f14 100644
--- a/source/gameengine/Ketsji/KX_PythonInit.cpp
+++ b/source/gameengine/Ketsji/KX_PythonInit.cpp
@@ -87,6 +87,7 @@ extern "C" {
#include "KX_SCA_DynamicActuator.h"
#include "KX_SteeringActuator.h"
#include "KX_NavMeshObject.h"
+#include "KX_MouseActuator.h"
#include "SCA_IInputDevice.h"
#include "SCA_PropertySensor.h"
@@ -1540,6 +1541,8 @@ PyObject *initGameLogic(KX_KetsjiEngine *engine, KX_Scene* scene) // quick hack
KX_MACRO_addTypesToDict(d, KX_PROPSENSOR_INTERVAL, SCA_PropertySensor::KX_PROPSENSOR_INTERVAL);
KX_MACRO_addTypesToDict(d, KX_PROPSENSOR_CHANGED, SCA_PropertySensor::KX_PROPSENSOR_CHANGED);
KX_MACRO_addTypesToDict(d, KX_PROPSENSOR_EXPRESSION, SCA_PropertySensor::KX_PROPSENSOR_EXPRESSION);
+ KX_MACRO_addTypesToDict(d, KX_PROPSENSOR_LESSTHAN, SCA_PropertySensor::KX_PROPSENSOR_LESSTHAN);
+ KX_MACRO_addTypesToDict(d, KX_PROPSENSOR_GREATERTHAN, SCA_PropertySensor::KX_PROPSENSOR_GREATERTHAN);
/* 3. Constraint actuator */
KX_MACRO_addTypesToDict(d, KX_CONSTRAINTACT_LOCX, KX_ConstraintActuator::KX_ACT_CONSTRAINT_LOCX);
@@ -1820,6 +1823,12 @@ PyObject *initGameLogic(KX_KetsjiEngine *engine, KX_Scene* scene) // quick hack
KX_MACRO_addTypesToDict(d, KX_ACTION_BLEND_BLEND, BL_Action::ACT_BLEND_BLEND);
KX_MACRO_addTypesToDict(d, KX_ACTION_BLEND_ADD, BL_Action::ACT_BLEND_ADD);
+ /* Mouse Actuator object axis*/
+ KX_MACRO_addTypesToDict(d, KX_ACT_MOUSE_OBJECT_AXIS_X, KX_MouseActuator::KX_ACT_MOUSE_OBJECT_AXIS_X);
+ KX_MACRO_addTypesToDict(d, KX_ACT_MOUSE_OBJECT_AXIS_Y, KX_MouseActuator::KX_ACT_MOUSE_OBJECT_AXIS_Y);
+ KX_MACRO_addTypesToDict(d, KX_ACT_MOUSE_OBJECT_AXIS_Z, KX_MouseActuator::KX_ACT_MOUSE_OBJECT_AXIS_Z);
+
+
// Check for errors
if (PyErr_Occurred())
{
diff --git a/source/gameengine/Ketsji/KX_PythonInitTypes.cpp b/source/gameengine/Ketsji/KX_PythonInitTypes.cpp
index bacace9199a..7d38ce58eee 100644
--- a/source/gameengine/Ketsji/KX_PythonInitTypes.cpp
+++ b/source/gameengine/Ketsji/KX_PythonInitTypes.cpp
@@ -97,6 +97,7 @@
#include "SCA_RandomActuator.h"
#include "SCA_IController.h"
#include "KX_NavMeshObject.h"
+#include "KX_MouseActuator.h"
static void PyType_Attr_Set(PyGetSetDef *attr_getset, PyAttributeDef *attr)
{
@@ -227,6 +228,7 @@ void initPyTypes(void)
PyType_Ready_Attr(dict, KX_VehicleWrapper, init_getset);
PyType_Ready_Attr(dict, KX_VertexProxy, init_getset);
PyType_Ready_Attr(dict, KX_VisibilityActuator, init_getset);
+ PyType_Ready_Attr(dict, KX_MouseActuator, init_getset);
PyType_Ready_Attr(dict, PyObjectPlus, init_getset);
PyType_Ready_Attr(dict, SCA_2DFilterActuator, init_getset);
PyType_Ready_Attr(dict, SCA_ANDController, init_getset);
diff --git a/source/gameengine/Ketsji/KX_RaySensor.h b/source/gameengine/Ketsji/KX_RaySensor.h
index 09e99fe0013..4604863a233 100644
--- a/source/gameengine/Ketsji/KX_RaySensor.h
+++ b/source/gameengine/Ketsji/KX_RaySensor.h
@@ -83,13 +83,14 @@ public:
}
//Python Interface
+ // odd order, see: SENS_RAY_X_AXIS
enum RayAxis {
- KX_RAY_AXIS_POS_X = 0,
- KX_RAY_AXIS_POS_Y,
- KX_RAY_AXIS_POS_Z,
- KX_RAY_AXIS_NEG_X,
- KX_RAY_AXIS_NEG_Y,
- KX_RAY_AXIS_NEG_Z
+ KX_RAY_AXIS_POS_X = 1,
+ KX_RAY_AXIS_POS_Y = 0,
+ KX_RAY_AXIS_POS_Z = 2,
+ KX_RAY_AXIS_NEG_X = 3,
+ KX_RAY_AXIS_NEG_Y = 4,
+ KX_RAY_AXIS_NEG_Z = 5,
};
#ifdef WITH_PYTHON
diff --git a/source/gameengine/Ketsji/KX_Scene.cpp b/source/gameengine/Ketsji/KX_Scene.cpp
index d06b9a04eb1..0dfdb3d5f3b 100644
--- a/source/gameengine/Ketsji/KX_Scene.cpp
+++ b/source/gameengine/Ketsji/KX_Scene.cpp
@@ -1616,7 +1616,7 @@ static void update_anim_thread_func(TaskPool *pool, void *taskdata, int UNUSED(t
gameobj->UpdateActionManager(curtime);
children = gameobj->GetChildren();
- if (gameobj->GetDeformer())
+ if (!gameobj->GetParent() && gameobj->GetDeformer())
gameobj->GetDeformer()->Update();
for (int j=0; j<children->GetCount(); ++j) {
diff --git a/source/gameengine/Ketsji/KX_TouchEventManager.cpp b/source/gameengine/Ketsji/KX_TouchEventManager.cpp
index d010d3d50a0..7ec379eec26 100644
--- a/source/gameengine/Ketsji/KX_TouchEventManager.cpp
+++ b/source/gameengine/Ketsji/KX_TouchEventManager.cpp
@@ -85,7 +85,7 @@ bool KX_TouchEventManager::newBroadphaseResponse(void *client_data,
PHY_IPhysicsController* ctrl2 = static_cast<PHY_IPhysicsController*>(object2);
KX_ClientObjectInfo *info1 = (ctrl1) ? static_cast<KX_ClientObjectInfo*>(ctrl1->GetNewClientInfo()) : NULL;
- KX_ClientObjectInfo *info2 = (ctrl1) ? static_cast<KX_ClientObjectInfo*>(ctrl2->GetNewClientInfo()) : NULL;
+ KX_ClientObjectInfo *info2 = (ctrl2) ? static_cast<KX_ClientObjectInfo*>(ctrl2->GetNewClientInfo()) : NULL;
// This call back should only be called for controllers of Near and Radar sensor
if (!info1)
@@ -97,9 +97,14 @@ bool KX_TouchEventManager::newBroadphaseResponse(void *client_data,
bool has_py_callbacks = false;
+#ifdef WITH_PYTHON
// Consider callbacks for broadphase inclusion if it's a sensor object type
if (gobj1 && gobj2)
has_py_callbacks = gobj1->m_collisionCallbacks || gobj2->m_collisionCallbacks;
+#else
+ (void)gobj1;
+ (void)gobj2;
+#endif
switch (info1->m_type)
{
diff --git a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLLight.cpp b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLLight.cpp
index 31935a64bf5..4ac1c9c4ebb 100644
--- a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLLight.cpp
+++ b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLLight.cpp
@@ -27,6 +27,9 @@
#include "glew-mx.h"
+#include <stdio.h>
+
+
#include "RAS_OpenGLLight.h"
#include "RAS_OpenGLRasterizer.h"
#include "RAS_ICanvas.h"
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 00000000000..fa0b86a2637
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,7 @@
+
+# Python CTests
+add_subdirectory(python)
+
+# GTest
+add_subdirectory(gtests)
+
diff --git a/source/tests/check_deprecated.py b/tests/check_deprecated.py
index bb9fcd818d2..d7193155b1c 100644
--- a/source/tests/check_deprecated.py
+++ b/tests/check_deprecated.py
@@ -25,7 +25,7 @@ DEPRECATE_DAYS = 120
SKIP_DIRS = ("extern",
"scons",
- os.path.join("source", "tests"), # not this dir
+ "tests", # not this dir
)
@@ -76,7 +76,7 @@ def deprecations():
"""
import datetime
- SOURCE_DIR = os.path.normpath(os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(__file__), "..", ".."))))
+ SOURCE_DIR = os.path.normpath(os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(__file__), ".."))))
SKIP_DIRS_ABS = [os.path.join(SOURCE_DIR, p) for p in SKIP_DIRS]
diff --git a/tests/gtests/CMakeLists.txt b/tests/gtests/CMakeLists.txt
new file mode 100644
index 00000000000..a3860ce3e67
--- /dev/null
+++ b/tests/gtests/CMakeLists.txt
@@ -0,0 +1,15 @@
+
+# GTest
+if(WITH_GTESTS)
+
+ Include(GTestTesting)
+
+ # Otherwise we get warnings here that we cant fix in external projects
+ remove_strict_flags()
+
+ add_subdirectory(testing)
+ add_subdirectory(blenlib)
+ add_subdirectory(guardedalloc)
+ add_subdirectory(bmesh)
+endif()
+
diff --git a/tests/gtests/blenlib/BLI_math_color_test.cc b/tests/gtests/blenlib/BLI_math_color_test.cc
new file mode 100644
index 00000000000..be6d7c6a55f
--- /dev/null
+++ b/tests/gtests/blenlib/BLI_math_color_test.cc
@@ -0,0 +1,90 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "BLI_math.h"
+
+TEST(mathutils, RGBToHSVRoundtrip)
+{
+ float orig_rgb[3] = {0.1f, 0.2f, 0.3f};
+ float hsv[3], rgb[3];
+ rgb_to_hsv_v(orig_rgb, hsv);
+ hsv_to_rgb_v(hsv, rgb);
+ EXPECT_V3_NEAR(orig_rgb, rgb, 1e-5);
+}
+
+TEST(mathutils, RGBToHSLRoundtrip)
+{
+ float orig_rgb[3] = {0.1f, 0.2f, 0.3f};
+ float hsl[3], rgb[3];
+ rgb_to_hsl_v(orig_rgb, hsl);
+ hsl_to_rgb_v(hsl, rgb);
+ EXPECT_V3_NEAR(orig_rgb, rgb, 1e-5);
+}
+
+TEST(mathutils, RGBToYUVRoundtrip)
+{
+ float orig_rgb[3] = {0.1f, 0.2f, 0.3f};
+ float yuv[3], rgb[3];
+ rgb_to_yuv(orig_rgb[0], orig_rgb[1], orig_rgb[2],
+ &yuv[0], &yuv[1], &yuv[2]);
+ yuv_to_rgb(yuv[0], yuv[1], yuv[2],
+ &rgb[0], &rgb[1], &rgb[2]);
+ EXPECT_V3_NEAR(orig_rgb, rgb, 1e-4);
+}
+
+TEST(mathutils, RGBToYCCRoundtrip)
+{
+ float orig_rgb[3] = {0.1f, 0.2f, 0.3f};
+ float ycc[3], rgb[3];
+
+ rgb_to_ycc(orig_rgb[0], orig_rgb[1], orig_rgb[2],
+ &ycc[0], &ycc[1], &ycc[2],
+ BLI_YCC_ITU_BT601);
+ ycc_to_rgb(ycc[0], ycc[1], ycc[2],
+ &rgb[0], &rgb[1], &rgb[2],
+ BLI_YCC_ITU_BT601);
+ EXPECT_V3_NEAR(orig_rgb, rgb, 1e-3);
+
+ rgb_to_ycc(orig_rgb[0], orig_rgb[1], orig_rgb[2],
+ &ycc[0], &ycc[1], &ycc[2],
+ BLI_YCC_ITU_BT709);
+ ycc_to_rgb(ycc[0], ycc[1], ycc[2],
+ &rgb[0], &rgb[1], &rgb[2],
+ BLI_YCC_ITU_BT709);
+ EXPECT_V3_NEAR(orig_rgb, rgb, 1e-3);
+
+ rgb_to_ycc(orig_rgb[0], orig_rgb[1], orig_rgb[2],
+ &ycc[0], &ycc[1], &ycc[2],
+ BLI_YCC_JFIF_0_255);
+ ycc_to_rgb(ycc[0], ycc[1], ycc[2],
+ &rgb[0], &rgb[1], &rgb[2],
+ BLI_YCC_JFIF_0_255);
+ EXPECT_V3_NEAR(orig_rgb, rgb, 1e-3);
+}
+
+TEST(mathutils, LinearRGBTosRGBNearZero)
+{
+ float linear_color = 0.002f;
+ float srgb_color = linearrgb_to_srgb(linear_color);
+ EXPECT_NEAR(0.02584f, srgb_color, 1e-5);
+}
+
+TEST(mathutils, LinearRGBTosRGB)
+{
+ float linear_color = 0.75f;
+ float srgb_color = linearrgb_to_srgb(linear_color);
+ EXPECT_NEAR(0.880824f, srgb_color, 1e-5);
+}
+
+TEST(mathutils, LinearRGBTosRGBRoundtrip)
+{
+ const int N = 50;
+ int i;
+ for (i = 0; i < N; ++i) {
+ float orig_linear_color = (float) i / N;
+ float srgb_color = linearrgb_to_srgb(orig_linear_color);
+ float linear_color = srgb_to_linearrgb(srgb_color);
+ EXPECT_NEAR(orig_linear_color, linear_color, 1e-5);
+ }
+}
diff --git a/tests/gtests/blenlib/BLI_math_geom_test.cc b/tests/gtests/blenlib/BLI_math_geom_test.cc
new file mode 100644
index 00000000000..2f85e6b4d7d
--- /dev/null
+++ b/tests/gtests/blenlib/BLI_math_geom_test.cc
@@ -0,0 +1,23 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "BLI_math.h"
+
+TEST(mathutils, DistToLine2DSimple)
+{
+ float p[2] = {5.0f, 1.0f},
+ a[2] = {0.0f, 0.0f},
+ b[2] = {2.0f, 0.0f};
+ float distance = dist_to_line_v2(p, a, b);
+ EXPECT_NEAR(1.0f, distance, 1e-6);
+}
+
+TEST(mathutils, DistToLineSegment2DSimple)
+{
+ float p[2] = {3.0f, 1.0f},
+ a[2] = {0.0f, 0.0f},
+ b[2] = {2.0f, 0.0f};
+ float distance = dist_to_line_segment_v2(p, a, b);
+ EXPECT_NEAR(sqrtf(2.0f), distance, 1e-6);
+}
diff --git a/tests/gtests/blenlib/BLI_path_util_test.cc b/tests/gtests/blenlib/BLI_path_util_test.cc
new file mode 100644
index 00000000000..e3a3699e5c1
--- /dev/null
+++ b/tests/gtests/blenlib/BLI_path_util_test.cc
@@ -0,0 +1,221 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+extern "C" {
+#include "BLI_fileops.h"
+#include "BLI_path_util.h"
+#include "../../../source/blender/imbuf/IMB_imbuf.h"
+}
+
+/* -------------------------------------------------------------------- */
+/* stubs */
+
+extern "C" {
+
+const char *GHOST_getUserDir(int version, const char *versionstr);
+const char *GHOST_getSystemDir(int version, const char *versionstr);
+#ifdef __linux__
+char *zLhm65070058860608_br_find_exe(const char *default_exe);
+#endif
+
+const char *GHOST_getUserDir(int version, const char *versionstr)
+{
+ return "/home/user";
+}
+
+const char *GHOST_getSystemDir(int version, const char *versionstr)
+{
+ return "/system/path";
+}
+
+struct ImBuf;
+void IMB_freeImBuf(struct ImBuf *ibuf) {}
+
+#ifdef __linux__
+char *zLhm65070058860608_br_find_exe(const char *default_exe)
+{
+ return NULL;
+}
+#endif
+
+}
+
+
+/* -------------------------------------------------------------------- */
+/* tests */
+
+/* BLI_cleanup_path */
+TEST(pathutils, PathUtilClean)
+{
+ /* "/./" -> "/" */
+ {
+ char path[FILE_MAX] = "/a/./b/./c/./";
+ BLI_cleanup_path(NULL, path);
+ EXPECT_STREQ("/a/b/c/", path);
+ }
+
+ {
+ char path[FILE_MAX] = "/./././";
+ BLI_cleanup_path(NULL, path);
+ EXPECT_STREQ("/", path);
+ }
+
+ {
+ char path[FILE_MAX] = "/a/./././b/";
+ BLI_cleanup_path(NULL, path);
+ EXPECT_STREQ("/a/b/", path);
+ }
+
+ /* "//" -> "/" */
+ {
+ char path[FILE_MAX] = "a////";
+ BLI_cleanup_path(NULL, path);
+ EXPECT_STREQ("a/", path);
+ }
+
+ if (0) /* FIXME */
+ {
+ char path[FILE_MAX] = "./a////";
+ BLI_cleanup_path(NULL, path);
+ EXPECT_STREQ("./a/", path);
+ }
+
+ /* "foo/bar/../" -> "foo/" */
+ {
+ char path[FILE_MAX] = "/a/b/c/../../../";
+ BLI_cleanup_path(NULL, path);
+ EXPECT_STREQ("/", path);
+ }
+
+ {
+ char path[FILE_MAX] = "/a/../a/b/../b/c/../c/";
+ BLI_cleanup_path(NULL, path);
+ EXPECT_STREQ("/a/b/c/", path);
+ }
+
+ {
+ char path[FILE_MAX] = "//../";
+ BLI_cleanup_path("/a/b/c/", path);
+ EXPECT_STREQ("/a/b/", path);
+ }
+}
+
+/* BLI_path_frame */
+TEST(pathutils, PathUtilFrame)
+{
+ bool ret;
+
+ {
+ char path[FILE_MAX] = "";
+ ret = BLI_path_frame(path, 123, 1);
+ EXPECT_EQ(1, ret);
+ EXPECT_STREQ("123", path);
+ }
+
+ {
+ char path[FILE_MAX] = "";
+ ret = BLI_path_frame(path, 123, 12);
+ EXPECT_EQ(1, ret);
+ EXPECT_STREQ("000000000123", path);
+ }
+
+ {
+ char path[FILE_MAX] = "test_";
+ ret = BLI_path_frame(path, 123, 1);
+ EXPECT_EQ(1, ret);
+ EXPECT_STREQ("test_123", path);
+ }
+
+ {
+ char path[FILE_MAX] = "test_";
+ ret = BLI_path_frame(path, 1, 12);
+ EXPECT_EQ(1, ret);
+ EXPECT_STREQ("test_000000000001", path);
+ }
+
+ {
+ char path[FILE_MAX] = "test_############";
+ ret = BLI_path_frame(path, 1, 0);
+ EXPECT_EQ(1, ret);
+ EXPECT_STREQ("test_000000000001", path);
+ }
+
+ {
+ char path[FILE_MAX] = "test_#_#_middle";
+ ret = BLI_path_frame(path, 123, 0);
+ EXPECT_EQ(1, ret);
+ EXPECT_STREQ("test_#_123_middle", path);
+ }
+
+ /* intentionally fail */
+ {
+ char path[FILE_MAX] = "";
+ ret = BLI_path_frame(path, 123, 0);
+ EXPECT_EQ(0, ret);
+ EXPECT_STREQ("", path);
+ }
+
+ {
+ char path[FILE_MAX] = "test_middle";
+ ret = BLI_path_frame(path, 123, 0);
+ EXPECT_EQ(0, ret);
+ EXPECT_STREQ("test_middle", path);
+ }
+}
+
+/* BLI_split_dirfile */
+TEST(pathutils, PathUtilSplitDirfile)
+{
+ {
+ const char *path = "";
+ char dir[FILE_MAX], file[FILE_MAX];
+ BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
+ EXPECT_STREQ("", dir);
+ EXPECT_STREQ("", file);
+ }
+
+ {
+ const char *path = "/";
+ char dir[FILE_MAX], file[FILE_MAX];
+ BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
+ EXPECT_STREQ("/", dir);
+ EXPECT_STREQ("", file);
+ }
+
+ {
+ const char *path = "fileonly";
+ char dir[FILE_MAX], file[FILE_MAX];
+ BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
+ EXPECT_STREQ("", dir);
+ EXPECT_STREQ("fileonly", file);
+ }
+
+ {
+ const char *path = "dironly/";
+ char dir[FILE_MAX], file[FILE_MAX];
+ BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
+ EXPECT_STREQ("dironly/", dir);
+ EXPECT_STREQ("", file);
+ }
+
+ {
+ const char *path = "/a/b";
+ char dir[FILE_MAX], file[FILE_MAX];
+ BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
+ EXPECT_STREQ("/a/", dir);
+ EXPECT_STREQ("b", file);
+ }
+
+ {
+ const char *path = "/dirtoobig/filetoobig";
+ char dir[5], file[5];
+ BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
+ EXPECT_STREQ("/dir", dir);
+ EXPECT_STREQ("file", file);
+
+ BLI_split_dirfile(path, dir, file, 1, 1);
+ EXPECT_STREQ("", dir);
+ EXPECT_STREQ("", file);
+ }
+}
diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt
new file mode 100644
index 00000000000..4980ca2e8ad
--- /dev/null
+++ b/tests/gtests/blenlib/CMakeLists.txt
@@ -0,0 +1,39 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2014, Blender Foundation
+# All rights reserved.
+#
+# Contributor(s): Sergey Sharybin
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ ../
+ ../../../source/blender/blenlib
+ ../../../intern/guardedalloc
+)
+
+include_directories(${INC})
+
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_LINKFLAGS}")
+set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${PLATFORM_LINKFLAGS_DEBUG}")
+
+
+BLENDER_TEST(BLI_math_color "bf_blenlib")
+BLENDER_TEST(BLI_math_geom "bf_blenlib")
+BLENDER_TEST(BLI_path_util "bf_blenlib;extern_wcwidth;${ZLIB_LIBRARIES}")
diff --git a/tests/gtests/bmesh/CMakeLists.txt b/tests/gtests/bmesh/CMakeLists.txt
new file mode 100644
index 00000000000..b1166a67bcd
--- /dev/null
+++ b/tests/gtests/bmesh/CMakeLists.txt
@@ -0,0 +1,50 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2014, Blender Foundation
+# All rights reserved.
+#
+# Contributor(s): Howard Trickey
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ ../
+ ../../../source/blender/blenlib
+ ../../../source/blender/makesdna
+ ../../../source/blender/bmesh
+ ../../../intern/guardedalloc
+)
+
+include_directories(${INC})
+
+setup_libdirs()
+get_property(BLENDER_SORTED_LIBS GLOBAL PROPERTY BLENDER_SORTED_LIBS_PROP)
+
+# Current BLENDER_SORTED_LIBS works with starting list of symbols in creator, but not
+# for this test. Doubling the list does let all the symbols be resolved, but link time is a bit painful.
+set(BLENDER_SORTED_LIBS ${BLENDER_SORTED_LIBS} ${BLENDER_SORTED_LIBS})
+
+if(WITH_BUILDINFO)
+ set(_buildinfo_src "$<TARGET_OBJECTS:buildinfoobj>")
+else()
+ set(_buildinfo_src "")
+endif()
+BLENDER_SRC_GTEST(bmesh_core "bmesh_core_test.cc;${_buildinfo_src}" "${BLENDER_SORTED_LIBS}")
+unset(_buildinfo_src)
+
+setup_liblinks(bmesh_core_test)
diff --git a/tests/gtests/bmesh/bmesh_core_test.cc b/tests/gtests/bmesh/bmesh_core_test.cc
new file mode 100644
index 00000000000..9c389a802de
--- /dev/null
+++ b/tests/gtests/bmesh/bmesh_core_test.cc
@@ -0,0 +1,36 @@
+#include "testing/testing.h"
+
+#include "BLI_utildefines.h"
+#include "bmesh.h"
+#include "BLI_math.h"
+
+TEST(bmesh_core, BMVertCreate) {
+ BMesh *bm;
+ BMVert *bv1, *bv2, *bv3;
+ const float co1[3] = {1.0f, 2.0f, 0.0f};
+
+ bm = BM_mesh_create(&bm_mesh_allocsize_default);
+ EXPECT_EQ(0, bm->totvert);
+ /* make a custom layer so we can see if it is copied properly */
+ BM_data_layer_add(bm, &bm->vdata, CD_PROP_FLT);
+ bv1 = BM_vert_create(bm, co1, NULL, BM_CREATE_NOP);
+ ASSERT_TRUE(bv1 != NULL);
+ EXPECT_EQ(1.0f, bv1->co[0]);
+ EXPECT_EQ(2.0f, bv1->co[1]);
+ EXPECT_EQ(0.0f, bv1->co[2]);
+ EXPECT_TRUE(is_zero_v3(bv1->no));
+ EXPECT_EQ((char)BM_VERT, bv1->head.htype);
+ EXPECT_EQ(0, bv1->head.hflag);
+ EXPECT_EQ(0, bv1->head.api_flag);
+ bv2 = BM_vert_create(bm, NULL, NULL, BM_CREATE_NOP);
+ ASSERT_TRUE(bv2 != NULL);
+ EXPECT_TRUE(is_zero_v3(bv2->co));
+ /* create with example should copy custom data but not select flag */
+ BM_vert_select_set(bm, bv2, true);
+ BM_elem_float_data_set(&bm->vdata, bv2, CD_PROP_FLT, 1.5f);
+ bv3 = BM_vert_create(bm, co1, bv2, BM_CREATE_NOP);
+ ASSERT_TRUE(bv3 != NULL);
+ EXPECT_FALSE(BM_elem_flag_test((BMElem *)bv3, BM_ELEM_SELECT));
+ EXPECT_EQ(1.5f, BM_elem_float_data_get(&bm->vdata, bv3, CD_PROP_FLT));
+ EXPECT_EQ(3, BM_mesh_elem_count(bm, BM_VERT));
+}
diff --git a/tests/gtests/guardedalloc/CMakeLists.txt b/tests/gtests/guardedalloc/CMakeLists.txt
new file mode 100644
index 00000000000..c09f8729ab0
--- /dev/null
+++ b/tests/gtests/guardedalloc/CMakeLists.txt
@@ -0,0 +1,36 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2014, Blender Foundation
+# All rights reserved.
+#
+# Contributor(s): Sergey Sharybin
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ ../
+ ../../../intern/guardedalloc
+)
+
+include_directories(${INC})
+
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_LINKFLAGS}")
+set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${PLATFORM_LINKFLAGS_DEBUG}")
+
+
+BLENDER_TEST(guardedalloc_alignment "")
diff --git a/tests/gtests/guardedalloc/guardedalloc_alignment_test.cc b/tests/gtests/guardedalloc/guardedalloc_alignment_test.cc
new file mode 100644
index 00000000000..38305b388e0
--- /dev/null
+++ b/tests/gtests/guardedalloc/guardedalloc_alignment_test.cc
@@ -0,0 +1,53 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "MEM_guardedalloc.h"
+
+#define CHECK_ALIGNMENT(ptr, align) EXPECT_EQ(0, (size_t)ptr % align)
+
+namespace {
+
+void DoBasicAlignmentChecks(const int alignment)
+{
+ int *foo, *bar;
+
+ foo = (int *) MEM_mallocN_aligned(sizeof(int) * 10, alignment, "test");
+ CHECK_ALIGNMENT(foo, alignment);
+
+ bar = (int *) MEM_dupallocN(foo);
+ CHECK_ALIGNMENT(bar, alignment);
+ MEM_freeN(bar);
+
+ foo = (int *) MEM_reallocN(foo, sizeof(int) * 5);
+ CHECK_ALIGNMENT(foo, alignment);
+
+ foo = (int *) MEM_recallocN(foo, sizeof(int) * 5);
+ CHECK_ALIGNMENT(foo, alignment);
+
+ MEM_freeN(foo);
+}
+
+} // namespace
+
+TEST(guardedalloc, LockfreeAlignedAlloc16)
+{
+ DoBasicAlignmentChecks(16);
+}
+
+TEST(guardedalloc, GuardedAlignedAlloc16)
+{
+ MEM_use_guarded_allocator();
+ DoBasicAlignmentChecks(16);
+}
+
+// On Apple we currently support 16 bit alignment only.
+// Harmless for Blender, but would be nice to support
+// eventually.
+#ifndef __APPLE__
+TEST(guardedalloc, GuardedAlignedAlloc32)
+{
+ MEM_use_guarded_allocator();
+ DoBasicAlignmentChecks(16);
+}
+#endif
diff --git a/tests/gtests/testing/CMakeLists.txt b/tests/gtests/testing/CMakeLists.txt
new file mode 100644
index 00000000000..93a3049efd8
--- /dev/null
+++ b/tests/gtests/testing/CMakeLists.txt
@@ -0,0 +1,48 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2014, Blender Foundation
+# All rights reserved.
+#
+# Contributor(s): Sergey Sharybin
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ ../
+ ../../../extern/libmv/third_party/gflags
+ ../../../extern/gtest/include
+)
+
+if(WIN32)
+ list(APPEND INC
+ ../../../extern/libmv/third_party/glog/src/windows
+ )
+else()
+ list(APPEND INC
+ ../../../extern/libmv/third_party/glog/src
+ )
+endif()
+
+set(INC_SYS
+)
+
+set(SRC
+ testing_main.cc
+)
+
+blender_add_lib(bf_testing_main "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/tests/gtests/testing/testing.h b/tests/gtests/testing/testing.h
new file mode 100644
index 00000000000..9083654f1d5
--- /dev/null
+++ b/tests/gtests/testing/testing.h
@@ -0,0 +1,78 @@
+#ifndef __BLENDER_TESTING_H__
+#define __BLENDER_TESTING_H__
+
+#include "glog/logging.h"
+#include "gflags/gflags.h"
+#include "gtest/gtest.h"
+
+#define EXPECT_V3_NEAR(a, b, eps) \
+ { \
+ EXPECT_NEAR(a[0], b[0], eps); \
+ EXPECT_NEAR(a[1], b[1], eps); \
+ EXPECT_NEAR(a[2], b[2], eps); \
+ } (void) 0
+
+#define EXPECT_MATRIX_NEAR(a, b, tolerance) \
+do { \
+ bool dims_match = (a.rows() == b.rows()) && (a.cols() == b.cols()); \
+ EXPECT_EQ(a.rows(), b.rows()) << "Matrix rows don't match."; \
+ EXPECT_EQ(a.cols(), b.cols()) << "Matrix cols don't match."; \
+ if (dims_match) { \
+ for (int r = 0; r < a.rows(); ++r) { \
+ for (int c = 0; c < a.cols(); ++c) { \
+ EXPECT_NEAR(a(r, c), b(r, c), tolerance) \
+ << "r=" << r << ", c=" << c << "."; \
+ } \
+ } \
+ } \
+} while(false);
+
+#define EXPECT_MATRIX_NEAR_ZERO(a, tolerance) \
+do { \
+ for (int r = 0; r < a.rows(); ++r) { \
+ for (int c = 0; c < a.cols(); ++c) { \
+ EXPECT_NEAR(0.0, a(r, c), tolerance) \
+ << "r=" << r << ", c=" << c << "."; \
+ } \
+ } \
+} while(false);
+
+#define EXPECT_MATRIX_EQ(a, b) \
+do { \
+ bool dims_match = (a.rows() == b.rows()) && (a.cols() == b.cols()); \
+ EXPECT_EQ(a.rows(), b.rows()) << "Matrix rows don't match."; \
+ EXPECT_EQ(a.cols(), b.cols()) << "Matrix cols don't match."; \
+ if (dims_match) { \
+ for (int r = 0; r < a.rows(); ++r) { \
+ for (int c = 0; c < a.cols(); ++c) { \
+ EXPECT_EQ(a(r, c), b(r, c)) \
+ << "r=" << r << ", c=" << c << "."; \
+ } \
+ } \
+ } \
+} while(false);
+
+// Check that sin(angle(a, b)) < tolerance.
+#define EXPECT_MATRIX_PROP(a, b, tolerance) \
+do { \
+ bool dims_match = (a.rows() == b.rows()) && (a.cols() == b.cols()); \
+ EXPECT_EQ(a.rows(), b.rows()) << "Matrix rows don't match."; \
+ EXPECT_EQ(a.cols(), b.cols()) << "Matrix cols don't match."; \
+ if (dims_match) { \
+ double c = CosinusBetweenMatrices(a, b); \
+ if (c * c < 1) { \
+ double s = sqrt(1 - c * c); \
+ EXPECT_NEAR(0, s, tolerance); \
+ } \
+ } \
+} while(false);
+
+#ifdef LIBMV_NUMERIC_NUMERIC_H
+template<class TMat>
+double CosinusBetweenMatrices(const TMat &a, const TMat &b) {
+ return (a.array() * b.array()).sum() /
+ libmv::FrobeniusNorm(a) / libmv::FrobeniusNorm(b);
+}
+#endif
+
+#endif // __BLENDER_TESTING_H__
diff --git a/tests/gtests/testing/testing_main.cc b/tests/gtests/testing/testing_main.cc
new file mode 100644
index 00000000000..ef8e743b642
--- /dev/null
+++ b/tests/gtests/testing/testing_main.cc
@@ -0,0 +1,36 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2014 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "testing/testing.h"
+
+int main(int argc, char **argv) {
+ testing::InitGoogleTest(&argc, argv);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ google::InitGoogleLogging(argv[0]);
+
+ return RUN_ALL_TESTS();
+}
+
diff --git a/source/tests/CMakeLists.txt b/tests/python/CMakeLists.txt
index 85c68693792..85c68693792 100644
--- a/source/tests/CMakeLists.txt
+++ b/tests/python/CMakeLists.txt
diff --git a/source/tests/batch_import.py b/tests/python/batch_import.py
index a2c5fb59055..dea08b45c3a 100644
--- a/source/tests/batch_import.py
+++ b/tests/python/batch_import.py
@@ -21,21 +21,21 @@
"""
Example Usage:
-./blender.bin --background --python source/tests/batch_import.py -- \
+./blender.bin --background --python tests/python/batch_import.py -- \
--operator="bpy.ops.import_scene.obj" \
--path="/fe/obj" \
--match="*.obj" \
--start=0 --end=10 \
--save_path=/tmp/test
-./blender.bin --background --python source/tests/batch_import.py -- \
+./blender.bin --background --python tests/python/batch_import.py -- \
--operator="bpy.ops.import_scene.autodesk_3ds" \
--path="/fe/" \
--match="*.3ds" \
--start=0 --end=1000 \
--save_path=/tmp/test
-./blender.bin --background --addons io_curve_svg --python source/tests/batch_import.py -- \
+./blender.bin --background --addons io_curve_svg --python tests/python/batch_import.py -- \
--operator="bpy.ops.import_curve.svg" \
--path="/usr/" \
--match="*.svg" \
diff --git a/source/tests/bl_keymap_completeness.py b/tests/python/bl_keymap_completeness.py
index 00322907f69..00322907f69 100644
--- a/source/tests/bl_keymap_completeness.py
+++ b/tests/python/bl_keymap_completeness.py
diff --git a/source/tests/bl_load_addons.py b/tests/python/bl_load_addons.py
index f04ae64ee73..f04ae64ee73 100644
--- a/source/tests/bl_load_addons.py
+++ b/tests/python/bl_load_addons.py
diff --git a/source/tests/bl_load_py_modules.py b/tests/python/bl_load_py_modules.py
index 9677397e01d..9677397e01d 100644
--- a/source/tests/bl_load_py_modules.py
+++ b/tests/python/bl_load_py_modules.py
diff --git a/source/tests/bl_mesh_modifiers.py b/tests/python/bl_mesh_modifiers.py
index 2f342f2c65e..b3f77aed96b 100644
--- a/source/tests/bl_mesh_modifiers.py
+++ b/tests/python/bl_mesh_modifiers.py
@@ -25,7 +25,7 @@
# Later, we may have a way to check the results are valid.
-# ./blender.bin --factory-startup --python source/tests/bl_mesh_modifiers.py
+# ./blender.bin --factory-startup --python tests/python/bl_mesh_modifiers.py
#
import math
diff --git a/source/tests/bl_mesh_validate.py b/tests/python/bl_mesh_validate.py
index ac5be7d08d7..ac5be7d08d7 100644
--- a/source/tests/bl_mesh_validate.py
+++ b/tests/python/bl_mesh_validate.py
diff --git a/source/tests/bl_pyapi_mathutils.py b/tests/python/bl_pyapi_mathutils.py
index c31244462cd..d204e58dbd4 100644
--- a/source/tests/bl_pyapi_mathutils.py
+++ b/tests/python/bl_pyapi_mathutils.py
@@ -1,4 +1,6 @@
-# ./blender.bin --background -noaudio --python source/tests/bl_pyapi_mathutils.py
+# Apache License, Version 2.0
+
+# ./blender.bin --background -noaudio --python tests/python/bl_pyapi_mathutils.py
import unittest
from test import support
from mathutils import Matrix, Vector
diff --git a/tests/python/bl_pyapi_units.py b/tests/python/bl_pyapi_units.py
new file mode 100644
index 00000000000..478adef51b2
--- /dev/null
+++ b/tests/python/bl_pyapi_units.py
@@ -0,0 +1,82 @@
+# Apache License, Version 2.0
+
+# ./blender.bin --background -noaudio --python tests/python/bl_pyapi_units.py
+import unittest
+from test import support
+
+from bpy.utils import units
+
+class UnitsTesting(unittest.TestCase):
+ # From user typing to 'internal' Blender value.
+ INPUT_TESTS = (
+ # system, type, ref, input, value
+ ##### LENGTH
+ ('IMPERIAL', 'LENGTH', "", "1ft", 0.3048),
+ ('IMPERIAL', 'LENGTH', "", "(1+1)ft", 0.3048 * 2),
+ ('IMPERIAL', 'LENGTH', "", "1mi4\"", 1609.344 + 0.0254 * 4),
+ ('METRIC', 'LENGTH', "", "0.005µm", 0.000001 * 0.005),
+ ('METRIC', 'LENGTH', "", "1e6km", 1000.0 * 1e6),
+ ('IMPERIAL', 'LENGTH', "", "1ft5cm", 0.3048 + 0.01 * 5),
+ ('METRIC', 'LENGTH', "", "1ft5cm", 0.3048 + 0.01 * 5),
+ # Using reference string to find a unit when none is given.
+ ('IMPERIAL', 'LENGTH', "33.3ft", "1", 0.3048),
+ ('METRIC', 'LENGTH', "33.3dm", "1", 0.1),
+ ('IMPERIAL', 'LENGTH', "33.3cm", "1", 0.3048), # ref unit is not in IMPERIAL system, default to feet...
+ ('IMPERIAL', 'LENGTH', "33.3ft", "1\"", 0.0254), # unused ref unit, since one is given already!
+ #('IMPERIAL', 'LENGTH', "", "1+1ft", 0.3048 * 2), # Will fail with current code!
+ )
+
+ # From 'internal' Blender value to user-friendly printing
+ OUTPUT_TESTS = (
+ # system, type, prec, sep, compat, value, output
+ ##### LENGTH
+ ('IMPERIAL', 'LENGTH', 3, False, False, 0.3048, "1'"),
+ ('IMPERIAL', 'LENGTH', 3, False, True, 0.3048, "1ft"),
+ ('IMPERIAL', 'LENGTH', 3, True, False, 0.3048 * 2 + 0.0254 * 5.5, "2' 5.5\""),
+ # Those next two fail, here again because precision ignores order magnitude :/
+ #('IMPERIAL', 'LENGTH', 3, False, False, 1609.344 * 1e6, "1000000mi"), # == 1000000.004mi!!!
+ #('IMPERIAL', 'LENGTH', 6, False, False, 1609.344 * 1e6, "1000000mi"), # == 1000000.003641mi!!!
+ ('METRIC', 'LENGTH', 3, True, False, 1000 * 2 + 0.001 * 15, "2km 1.5cm"),
+ ('METRIC', 'LENGTH', 3, True, False, 1234.56789, "1km 234.6m"),
+ # Note: precision seems basically unused when using multi units!
+ ('METRIC', 'LENGTH', 9, True, False, 1234.56789, "1km 234.6m"),
+ ('METRIC', 'LENGTH', 9, False, False, 1234.56789, "1.23456789km"),
+ ('METRIC', 'LENGTH', 9, True, False, 1000.000123456789, "1km 0.1mm"),
+ )
+
+ def test_units_inputs(self):
+ # Stolen from FBX addon!
+ def similar_values(v1, v2, e):
+ if v1 == v2:
+ return True
+ return ((abs(v1 - v2) / max(abs(v1), abs(v2))) <= e)
+
+ for usys, utype, ref, inpt, val in self.INPUT_TESTS:
+ opt_val = units.to_value(usys, utype, inpt, ref)
+ # Note: almostequal is not good here, precision is fixed on decimal digits, not variable with
+ # magnitude of numbers (i.e. 1609.4416 ~= 1609.4456 fails even at 5 of 'places'...).
+ self.assertTrue(similar_values(opt_val, val, 1e-7),
+ msg="%s, %s: \"%s\" (ref: \"%s\") => %f, expected %f"
+ "" % (usys, utype, inpt, ref, opt_val, val))
+
+ def test_units_outputs(self):
+ for usys, utype, prec, sep, compat, val, output in self.OUTPUT_TESTS:
+ opt_str = units.to_string(usys, utype, val, prec, sep, compat)
+ self.assertEqual(opt_str, output,
+ msg="%s, %s: %f (precision: %d, separate units: %d, compat units: %d) => "
+ "\"%s\", expected \"%s\"" % (usys, utype, val, prec, sep, compat, opt_str, output))
+
+
+def test_main():
+ try:
+ support.run_unittest(UnitsTesting)
+ except:
+ import traceback
+ traceback.print_exc()
+
+ # alert CTest we failed
+ import sys
+ sys.exit(1)
+
+if __name__ == '__main__':
+ test_main()
diff --git a/source/tests/bl_rna_wiki_reference.py b/tests/python/bl_rna_wiki_reference.py
index 8cb20aaf7f6..5781c53c045 100644
--- a/source/tests/bl_rna_wiki_reference.py
+++ b/tests/python/bl_rna_wiki_reference.py
@@ -19,7 +19,7 @@
# <pep8 compliant>
# Use for validating our wiki interlinking.
-# ./blender.bin --background -noaudio --python source/tests/bl_rna_wiki_reference.py
+# ./blender.bin --background -noaudio --python tests/python/bl_rna_wiki_reference.py
#
# 1) test_data() -- ensure the data we have is correct format
# 2) test_lookup_coverage() -- ensure that we have lookups for _every_ RNA path
diff --git a/source/tests/bl_rst_completeness.py b/tests/python/bl_rst_completeness.py
index 6e67f8d908d..d0ba2c552cf 100644
--- a/source/tests/bl_rst_completeness.py
+++ b/tests/python/bl_rst_completeness.py
@@ -20,11 +20,11 @@
# run this script in the game engine.
# or on the command line with...
-# ./blender.bin --background -noaudio --python source/tests/bl_rst_completeness.py
+# ./blender.bin --background -noaudio --python tests/python/bl_rst_completeness.py
# Paste this into the bge and run on an always actuator.
'''
-filepath = "/dsk/data/src/blender/blender/source/tests/bl_rst_completeness.py"
+filepath = "/src/blender/tests/python/bl_rst_completeness.py"
exec(compile(open(filepath).read(), filepath, 'exec'))
'''
diff --git a/source/tests/bl_run_operators.py b/tests/python/bl_run_operators.py
index e14b0ce6d32..e14b0ce6d32 100644
--- a/source/tests/bl_run_operators.py
+++ b/tests/python/bl_run_operators.py
diff --git a/source/tests/bl_test.py b/tests/python/bl_test.py
index 0cb322a21b1..0cb322a21b1 100644
--- a/source/tests/bl_test.py
+++ b/tests/python/bl_test.py
diff --git a/source/tests/pep8.py b/tests/python/pep8.py
index cca49d54ac0..7713ffeaaa4 100644
--- a/source/tests/pep8.py
+++ b/tests/python/pep8.py
@@ -32,7 +32,7 @@ import os
# in Debian install pylint pep8 with apt-get/aptitude/etc
#
# on *nix run
-# python source/tests/pep8.py > test_pep8.log 2>&1
+# python tests/pep8.py > test_pep8.log 2>&1
# how many lines to read into the file, pep8 comment
# should be directly after the license header, ~20 in most cases
diff --git a/source/tests/rna_array.py b/tests/python/rna_array.py
index a2241dff108..d3014868bcb 100644
--- a/source/tests/rna_array.py
+++ b/tests/python/rna_array.py
@@ -1,20 +1,4 @@
-# ##### BEGIN GPL LICENSE BLOCK #####
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# ##### END GPL LICENSE BLOCK #####
+# Apache License, Version 2.0
import unittest
import random
diff --git a/source/tests/rna_info_dump.py b/tests/python/rna_info_dump.py
index 40d7b7c38a6..c26d94a1246 100644
--- a/source/tests/rna_info_dump.py
+++ b/tests/python/rna_info_dump.py
@@ -19,7 +19,7 @@
# <pep8 compliant>
# Used for generating API diffs between releases
-# ./blender.bin --background -noaudio --python source/tests/rna_info_dump.py
+# ./blender.bin --background -noaudio --python tests/python/rna_info_dump.py
import bpy
diff --git a/source/tests/rst_to_doctree_mini.py b/tests/python/rst_to_doctree_mini.py
index 6a885a108f8..6a885a108f8 100644
--- a/source/tests/rst_to_doctree_mini.py
+++ b/tests/python/rst_to_doctree_mini.py